pull/51/head
sunface 4 years ago
parent 9a43a37cae
commit a5f9c744c2

@ -43,7 +43,7 @@ function PageContainer1(props: PageContainerProps) {
<Flex px={[0,0,16,16]}>
<VerticalNav display={{base:"none",md:"block"}} width={["100px","100px","200px","200px"]} />
<Nav display={{base:"block",md:"none"}} />
<Box width="100%" ml={["0px","0px","150px","150px"]} pb="8" p={["1","1","3","3"]} mt={["70px","70px","0px","0px"]} {...rest}>
<Box overflow="auto" width="100%" ml={["0px","0px","150px","150px"]} pb="8" p={["1","1","3","3"]} mt={["70px","70px","0px","0px"]} {...rest}>
{children}
</Box>
</Flex>

@ -12,14 +12,18 @@ import React, { useEffect, useState } from "react"
import { Story } from "src/types/story"
import { requestApi } from "utils/axios/request"
import StorySidebar from "components/story/story-sidebar"
import Series from "components/story/series"
import Card from "components/card"
const PostPage = () => {
const router = useRouter()
const id = router.query.post_id
const [post, setPost]: [Story, any] = useState(null)
const [series,setSeries] = useState([])
useEffect(() => {
if (id) {
getData()
getSeries()
}
}, [id])
@ -37,6 +41,10 @@ const PostPage = () => {
setPost(res.data)
}
const getSeries = async () => {
const res = await requestApi.get(`/story/series/byPostID/${id}`)
setSeries(res.data)
}
return (
<>
@ -60,6 +68,7 @@ const PostPage = () => {
</Box>
<HStack ml="2" spacing="3" mt="4">{post.rawTags.map(tag => <TagTextCard key={tag.id} tag={tag} />)}</HStack>
{series.length > 0 && <Card p="0" mt="4"><Series postID={post.id} series={series}/></Card>}
<Box mt="6" p="2"><Comments storyID={post.id} /></Box>
</Box>
<Box pt="16">

@ -95,7 +95,7 @@ import Empty from "components/empty"
</Card>
<Card p="0">
<Wrap pt="4" pb="1" pl="4" alignItems="center">
{<Wrap pt="4" pb="1" pl="4" alignItems="center">
{
tags.map(t =>
<HStack px="2" py="1" spacing="1" mr="3" cursor="pointer" key={t.id} className={t.id===filter?.id ?"tag-bg": null} onClick={() => filterPostsByTag(t)}>
@ -103,8 +103,8 @@ import Empty from "components/empty"
<Text fontSize=".9rem">{t.title}</Text>
</HStack>)
}
</Wrap>
<Divider mt="3" mb="5" />
</Wrap>}
{tags.length > 0&& <Divider mt="3" mb="5" />}
{posts.length !== 0
?
<Stories stories={posts} showFooter={false}/>

@ -19,11 +19,12 @@ import { IDType } from "src/types/id"
import PostSelect from "components/story/post-select"
import { cloneDeep, find, remove } from "lodash"
import userCustomTheme from "theme/user-custom"
import Tags from "components/tags/tags"
var validator = require('validator');
const newSeries: Story = { title: '', brief: '', cover: '', type: IDType.Series }
const PostsPage = () => {
const [currentSeries, setCurrentSeries] = useState(null)
const [currentSeries, setCurrentSeries]:[Story,any] = useState(null)
const [series, setSeries] = useState([])
const [posts, setPosts] = useState([])
const [seriesPosts, setSeriesPosts] = useState([])
@ -186,6 +187,15 @@ const PostsPage = () => {
</FormControl>
)}
</Field>
<Field>
{({ field, form }) => (
<FormControl>
<FormLabel></FormLabel>
<Tags tags={currentSeries.tags} onChange={(ids) => currentSeries.tags = ids}/>
</FormControl>
)}
</Field>
<Field>
{({ field, form }) => (
<FormControl isInvalid={form.errors.brief && form.touched.brief}>

@ -1,4 +1,4 @@
import { Box, Divider, Heading, HStack, Image} from "@chakra-ui/react"
import { Box, Divider, Heading, HStack, Image, Tag, Text, VStack } from "@chakra-ui/react"
import Comments from "components/comments/comments"
import { MarkdownRender } from "components/markdown-editor/render"
import { StoryAuthor } from "components/story/story-author"
@ -12,14 +12,18 @@ import React, { useEffect, useState } from "react"
import { Story } from "src/types/story"
import { requestApi } from "utils/axios/request"
import StorySidebar from "components/story/story-sidebar"
import Stroies from "components/story/stories"
import Card from "components/card"
const PostPage = () => {
const router = useRouter()
const id = router.query.id
const [post, setPost]: [Story, any] = useState(null)
const [series, setSeries]: [Story, any] = useState(null)
const [posts, setPosts]: [Story[], any] = useState([])
useEffect(() => {
if (id) {
getData()
getSeries()
getSeriesPost()
}
}, [id])
@ -32,11 +36,15 @@ const PostPage = () => {
}
}, [router])
const getData = async () => {
const getSeries = async () => {
const res = await requestApi.get(`/story/post/${id}`)
setPost(res.data)
setSeries(res.data)
}
const getSeriesPost = async () => {
const res = await requestApi.get(`/story/series/posts/${id}`)
setPosts(res.data)
}
return (
<>
@ -44,26 +52,33 @@ const PostPage = () => {
title={siteConfig.seo.title}
description={siteConfig.seo.description}
/>
{post && <PageContainer nav={<PostNav post={post} />} mt="2rem">
{series && <PageContainer nav={<PostNav post={series} />} mt="2rem">
<>
<HStack alignItems="top" spacing={[0, 0, 14, 14]}>
<Box width={["100%", "100%", "75%", "75%"]} height="fit-content" pl={[0, 0, "0%", "10%"]}>
<Image src={post.cover} />
<HStack alignItems="top" spacing={[0, 0, 14, 14]} mt="8">
<Box width={["100%", "100%", "75%", "75%"]} pl={[0, 0, "0%", "10%"]}>
<HStack alignItems="top">
<Box px="2">
<Heading size="lg" my="6" lineHeight="1.5">{post.title}</Heading>
<Image src={series.cover} display={{ base: "block", md: "none" }} mb="4" />
<Tag fontWeight="600">SERIES</Tag>
<Heading size="lg" my="3" lineHeight="1.5">{series.title}</Heading>
<Text>{series.brief}</Text>
<HStack spacing="3" mt="6">{series.rawTags.map(tag => <TagTextCard key={tag.id} tag={tag} />)}</HStack>
</Box>
{series.cover && <Image src={series.cover} width="400px" display={{ base: "none", md: "block" }} />}
<Divider my="4" />
<StoryAuthor story={post} />
<Divider my="4" />
</HStack>
<MarkdownRender md={post.md} py="2" mt="6" />
</Box>
<HStack ml="2" spacing="3" mt="4">{post.rawTags.map(tag => <TagTextCard key={tag.id} tag={tag} />)}</HStack>
<VStack mt="4">
<Divider mt="8" mb="5"/>
<Text position="relative" top="-41px" layerStyle="textSecondary">Articles in this series</Text>
</VStack>
<Box mt="6" p="2"><Comments storyID={post.id} /></Box>
<Card><Stroies stories={posts} showFooter={false} /></Card>
<Box mt="6"><Comments storyID={series.id} /></Box>
</Box>
<Box pt="16">
<StorySidebar story={post} />
<StorySidebar story={series} />
</Box>
</HStack>

@ -40,8 +40,8 @@ func DeletePost(c *gin.Context) {
return
}
user := user.CurrentUser(c)
if !models.IsStoryCreator(user.ID, id) {
u := user.CurrentUser(c)
if !models.IsStoryCreator(u.ID, id) {
c.JSON(http.StatusForbidden, common.RespError(e.NoPermission))
return
}
@ -57,16 +57,16 @@ func DeletePost(c *gin.Context) {
func GetStory(c *gin.Context) {
id := c.Param("id")
user := user.CurrentUser(c)
u := user.CurrentUser(c)
ar, err := story.GetStory(id, "")
if err != nil {
c.JSON(err.Status, common.RespError(err.Message))
return
}
if user != nil {
ar.Liked = interaction.GetLiked(ar.ID, user.ID)
ar.Bookmarked, _ = story.Bookmarked(user.ID, ar.ID)
if u != nil {
ar.Liked = interaction.GetLiked(ar.ID, u.ID)
ar.Bookmarked, _ = story.Bookmarked(u.ID, ar.ID)
}
c.JSON(http.StatusOK, common.RespSuccess(ar))
@ -85,9 +85,9 @@ func GenStoryID(c *gin.Context) {
func Bookmark(c *gin.Context) {
storyID := c.Param("storyID")
user := user.CurrentUser(c)
u := user.CurrentUser(c)
err := story.Bookmark(user.ID, storyID)
err := story.Bookmark(u.ID, storyID)
if err != nil {
c.JSON(err.Status, common.RespError(err.Message))
return
@ -137,6 +137,18 @@ func GetSeriesPost(c *gin.Context) {
c.JSON(http.StatusOK, common.RespSuccess(posts))
}
func GetSeriesPosts(c *gin.Context) {
id := c.Param("id")
u := user.CurrentUser(c)
posts, err := story.GetSeriesPosts(u, id)
if err != nil {
c.JSON(err.Status, common.RespError(err.Message))
return
}
c.JSON(http.StatusOK, common.RespSuccess(posts))
}
func DeleteSeriesPost(c *gin.Context) {
id := c.Param("id")
if id == "" {
@ -144,8 +156,8 @@ func DeleteSeriesPost(c *gin.Context) {
return
}
user := user.CurrentUser(c)
if !models.IsStoryCreator(user.ID, id) {
u := user.CurrentUser(c)
if !models.IsStoryCreator(u.ID, id) {
c.JSON(http.StatusForbidden, common.RespError(e.NoPermission))
return
}
@ -159,3 +171,29 @@ func DeleteSeriesPost(c *gin.Context) {
c.JSON(http.StatusOK, common.RespSuccess(nil))
}
func GetPostSeries(c *gin.Context) {
postID := c.Param("id")
series, err := story.GetPostSeries(postID)
if err != nil {
c.JSON(err.Status, common.RespError(err.Message))
return
}
c.JSON(http.StatusOK, common.RespSuccess(series))
}
func GetSeries(c *gin.Context) {
ids := make([]string, 0)
c.Bind(&ids)
u := user.CurrentUser(c)
series, err := story.GetSeries(u, ids)
if err != nil {
c.JSON(err.Status, common.RespError(err.Message))
return
}
c.JSON(http.StatusOK, common.RespSuccess(series))
}

@ -54,9 +54,12 @@ func (s *Server) Start() error {
r.GET("/story/posts/drafts", IsLogin(), api.GetEditorDrafts)
r.GET("/story/posts/home/:filter", api.GetHomePosts)
r.POST("/story", IsLogin(), api.SubmitStory)
r.POST("/story/series", api.GetSeries)
r.POST("/story/series/post/:id", IsLogin(), api.SubmitSeriesPost)
r.GET("/story/series/post/:id", api.GetSeriesPost)
r.DELETE("/story/series/post/:id", api.DeleteSeriesPost)
r.GET("/story/series/posts/:id", api.GetSeriesPosts)
r.GET("/story/series/byPostID/:id", api.GetPostSeries)
r.DELETE("/story/series/post/:id", IsLogin(), api.DeleteSeriesPost)
r.POST("/story/post/draft", IsLogin(), api.SubmitPostDraft)
r.DELETE("/story/post/:id", IsLogin(), api.DeletePost)
r.POST("/story/bookmark/:storyID", IsLogin(), api.Bookmark)

@ -14,9 +14,11 @@ import (
"github.com/imdotdev/im.dev/server/pkg/models"
)
const PostQueryPrefix = "select id,type,slug,title,url,cover,brief,creator,created,updated from story "
func HomePosts(user *models.User, filter string) (models.Stories, *e.Error) {
rows, err := db.Conn.Query("select id,type,slug,title,url,cover,brief,creator,created,updated from story where status=?", models.StatusPublished)
rows, err := db.Conn.Query(PostQueryPrefix+"where status=?", models.StatusPublished)
if err != nil && err != sql.ErrNoRows {
logger.Warn("get user posts error", "error", err)
return nil, e.New(http.StatusInternalServerError, e.Internal)
@ -32,9 +34,9 @@ func UserPosts(tp string, user *models.User, uid string) (models.Stories, *e.Err
var rows *sql.Rows
var err error
if tp == models.IDTypeUndefined {
rows, err = db.Conn.Query("select id,type,slug,title,url,cover,brief,creator,created,updated from story where creator=? and status=?", uid, models.StatusPublished)
rows, err = db.Conn.Query(PostQueryPrefix+"where creator=? and status=?", uid, models.StatusPublished)
} else {
rows, err = db.Conn.Query("select id,type,slug,title,url,cover,brief,creator,created,updated from story where creator=? and type=? and status=?", uid, tp, models.StatusPublished)
rows, err = db.Conn.Query(PostQueryPrefix+"where creator=? and type=? and status=?", uid, tp, models.StatusPublished)
}
if err != nil && err != sql.ErrNoRows {
@ -49,7 +51,7 @@ func UserPosts(tp string, user *models.User, uid string) (models.Stories, *e.Err
}
func UserDrafts(user *models.User, uid string) (models.Stories, *e.Error) {
rows, err := db.Conn.Query("select id,type,slug,title,url,cover,brief,creator,created,updated from story where creator=? and status=?", uid, models.StatusDraft)
rows, err := db.Conn.Query(PostQueryPrefix+"where creator=? and status=?", uid, models.StatusDraft)
if err != nil && err != sql.ErrNoRows {
logger.Warn("get user drafts error", "error", err)
return nil, e.New(http.StatusInternalServerError, e.Internal)
@ -71,7 +73,7 @@ func TagPosts(user *models.User, tagID string) (models.Stories, *e.Error) {
ids := strings.Join(postIDs, "','")
q := fmt.Sprintf("select id,type,slug,title,url,cover,brief,creator,created,updated from story where id in ('%s') and status='%d'", ids, models.StatusPublished)
q := fmt.Sprintf(PostQueryPrefix+"where id in ('%s') and status='%d'", ids, models.StatusPublished)
rows, err := db.Conn.Query(q)
if err != nil && err != sql.ErrNoRows {
logger.Warn("get user posts error", "error", err)
@ -101,7 +103,7 @@ func BookmarkPosts(user *models.User, filter string) (models.Stories, *e.Error)
ids := strings.Join(postIDs, "','")
q := fmt.Sprintf("select id,type,slug,title,url,cover,brief,creator,created,updated from story where id in ('%s')", ids)
q := fmt.Sprintf(PostQueryPrefix+"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)
@ -150,10 +152,11 @@ func GetPosts(user *models.User, rows *sql.Rows) models.Stories {
}
ar.Likes = interaction.GetLikes(ar.ID)
_, rawTags, err := tags.GetTargetTags(ar.ID)
tags, rawTags, err := tags.GetTargetTags(ar.ID)
if err != nil {
logger.Warn("get tags error", "error", err)
}
ar.Tags = tags
ar.RawTags = rawTags
posts = append(posts, ar)

@ -1,7 +1,11 @@
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"
@ -46,6 +50,50 @@ func GetSeriesPost(seriesID string) ([]*models.SeriesPost, *e.Error) {
return posts, nil
}
func GetSeriesPosts(user *models.User, seriesID string) ([]*models.Story, *e.Error) {
rows, err := db.Conn.Query("SELECT post_id,priority FROM series_post WHERE series_id=?", seriesID)
if err != nil {
logger.Warn("select series post error", "error", err)
return nil, e.New(http.StatusInternalServerError, e.Internal)
}
seriesPosts := make(models.SeriesPosts, 0)
seriesPostsMap := make(map[string]*models.SeriesPost)
postIDs := make([]string, 0)
for rows.Next() {
post := &models.SeriesPost{}
err := rows.Scan(&post.PostID, &post.Priority)
if err != nil {
logger.Warn("scan series post error", "error", err)
continue
}
seriesPosts = append(seriesPosts, post)
seriesPostsMap[post.PostID] = post
postIDs = append(postIDs, post.PostID)
}
ids := strings.Join(postIDs, "','")
q := fmt.Sprintf(PostQueryPrefix+"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 {
p, ok := seriesPostsMap[post.ID]
if ok {
post.Priority = p.Priority
}
}
sort.Sort(models.PriorityStories(posts))
return posts, nil
}
func DeleteSeriesPost(id string) *e.Error {
_, err := db.Conn.Exec("DELETE FROM series_post WHERE series_id=?", id)
if err != nil {
@ -54,3 +102,34 @@ func DeleteSeriesPost(id string) *e.Error {
return nil
}
func GetPostSeries(postID string) ([]string, *e.Error) {
series := make([]string, 0)
rows, err := db.Conn.Query("SELECT series_id FROM series_post WHERE post_id=?", postID)
if err != nil {
logger.Warn("get post series error", "error", err)
return nil, e.New(http.StatusInternalServerError, e.Internal)
}
for rows.Next() {
var id string
rows.Scan(&id)
series = append(series, id)
}
return series, nil
}
func GetSeries(user *models.User, seriesIds []string) ([]*models.Story, *e.Error) {
ids := strings.Join(seriesIds, "','")
q := fmt.Sprintf(PostQueryPrefix+"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)
}
series := GetPosts(user, rows)
return series, nil
}

@ -34,6 +34,8 @@ type Story struct {
Status int `json:"status"`
Created time.Time `json:"created"`
Updated time.Time `json:"updated"`
Priority int `json:"-"`
}
type Stories []*Story
@ -52,11 +54,27 @@ func (s FavorStories) Less(i, j int) bool {
return s[i].Likes > s[j].Likes
}
type PriorityStories []*Story
func (s PriorityStories) Len() int { return len(s) }
func (s PriorityStories) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s PriorityStories) Less(i, j int) bool {
return s[i].Priority < s[j].Priority
}
type SeriesPost struct {
PostID string `json:"id"`
Priority int `json:"priority"`
}
type SeriesPosts []*SeriesPost
func (s SeriesPosts) Len() int { return len(s) }
func (s SeriesPosts) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s SeriesPosts) Less(i, j int) bool {
return s[i].Priority < s[j].Priority
}
func IsStoryCreator(userID string, storyID string) bool {
var nid string
err := db.Conn.QueryRow("SELECT creator FROM story WHERE id=?", storyID).Scan(&nid)

@ -0,0 +1,117 @@
import React, { useEffect, useState } from "react"
import { Box, Flex, Heading, HStack, Image, Select, StackDivider, Tag, Text, useColorModeValue, VStack } from "@chakra-ui/react"
import { requestApi } from "utils/axios/request"
import userCustomTheme from "theme/user-custom"
import { Story } from "src/types/story"
import { find } from "lodash"
import { getStoryUrl } from "utils/story"
import Link from "next/link"
interface Props {
postID: string
series: string[]
}
export const Series = (props: Props) => {
const [showAll,setShowAll] = useState(false)
const [currentSeries, setCurrentSeries]: [Story, any] = useState(null)
const [series, setSeries]: [Story[], any] = useState([])
const [posts, setPosts]: [Story[], any] = useState([])
const borderColor = useColorModeValue(userCustomTheme.borderColor.light, userCustomTheme.borderColor.dark)
useEffect(() => {
getSeries()
}, [])
const getSeries = async () => {
const res = await requestApi.post(`/story/series`, props.series)
setSeries(res.data)
if (res.data.length > 0) {
setCurrentSeries(res.data[0])
getSeriesPosts(res.data[0].id)
}
}
const getSeriesPosts = async (id) => {
const res = await requestApi.get(`/story/series/posts/${id}`)
if (res.data.length > 4) {
setShowAll(false)
} else {
setShowAll(true)
}
setPosts(res.data)
}
const onSereisChange = e => {
const s = find(series, s => s.id === e.currentTarget.value)
setCurrentSeries(s)
getSeriesPosts(s.id)
}
const postInHidden = () => {
for (let i=0;i<posts.length;i++) {
if (posts[i].id === props.postID) {
if (i >= 2) {
return true
}
return false
}
}
return false
}
if (posts.length > 0) {
return (
<VStack divider={<StackDivider borderColor={borderColor} />} alignItems="left">
<Box p="4" pb="0">
<Text fontSize="sm" layerStyle="textSecondary">ARTICLE SERIES</Text>
{<Select onChange={e => onSereisChange(e)} value={currentSeries?.id} variant="unstyled" width="fit-content">
{
series.map(s => <option value={s.id} key={s.id}>{s.title}</option>)
}
</Select>}
</Box>
{
posts.map((post, i) => {
if (showAll) {
return <PostCard i={i} post={post} postID={props.postID} />
} else {
if (i < 2) {
return <PostCard i={i} post={post} postID={props.postID} />
}
}
})
}
{
(!showAll && posts.length > 2) && <HStack p="2" pb="4" spacing="4" cursor="pointer" onClick={() => setShowAll(true)}>
<Tag borderRadius="full" size="lg" colorScheme={postInHidden()? 'cyan' : null}>··</Tag>
<Heading size="sm" color={postInHidden()? 'cyan.500' : null}>Show all {posts.length-2} posts</Heading>
</HStack>
}
</VStack>
)
} else {
return null
}
}
export default Series
function PostCard({ i, post, postID }) {
return <Flex key={post.id} p="2" spacing="4" justifyContent="space-between" flexDirection={["column", "column", "row", "row"]}>
<HStack mr="4" spacing="4" alignItems="top">
<Tag borderRadius="full" size="lg" colorScheme={post.id === postID ? 'cyan' : null} height="fit-content">{i + 1}</Tag>
<Link href={getStoryUrl(post)}>
<Box cursor="pointer" pt="1">
<Heading size="sm">{post.title}</Heading>
<Text mt="3">{post.brief.substring(0, 100)}</Text>
</Box>
</Link>
</HStack>
{post.cover && <Image src={post.cover} mt={[4, 4, 0, 0]} height={["275px", "275px", "130px", "130px"]} width={["100%", "100%", "200px", "250px"]} />}
</Flex>
}

@ -5,6 +5,8 @@ import Link from "next/link"
import { FaHeart, FaRegComment, FaRegHeart } from "react-icons/fa"
import Bookmark from "./bookmark"
import { getCommentsUrl, getStoryUrl } from "utils/story"
import Like from "components/interaction/like"
import { getSvgIcon } from "components/svg-icon"
interface Props {
story: Story
@ -18,28 +20,22 @@ export const SimpleStoryCard = (props: Props) => {
const Layout = isLargeScreen ? HStack : VStack
return (
<VStack alignItems="left" spacing="0">
<Link href={getStoryUrl(story)}><Heading pb="2" size="sm" cursor="pointer">{story.title}</Heading></Link>
<Link href={getStoryUrl(story)}><Heading pb="2" fontSize=".9rem" cursor="pointer">{story.title}</Heading></Link>
<HStack pl="1" spacing="5" fontSize={size==='md'? '1rem' : ".9rem"}>
<Link href={`/${story.creator.username}`}><Text cursor="pointer">{story.creator.nickname}</Text></Link>
<HStack opacity="0.9">
{story.liked ?
<Box color="red.400"><FaHeart fontSize="1.1rem" /></Box>
:
<FaRegHeart fontSize="1.1rem" />}
<Text ml="2">{story.likes}</Text>
<Like liked={story.liked} count={story.likes} storyID={story.id} fontSize="1rem"/>
</HStack>
<a href={`${getCommentsUrl(story)}#comments`}>
<HStack opacity="0.9" cursor="pointer">
<FaRegComment fontSize="1.1rem" />
{getSvgIcon("comments", "1rem")}
<Text ml="2">{story.comments}</Text>
</HStack>
</a>
<Box style={{marginLeft: '4px'}}><Bookmark storyID={story.id} bookmarked={story.bookmarked} height=".95rem"/></Box>
<Box style={{marginLeft: '4px'}}><Bookmark storyID={story.id} bookmarked={story.bookmarked} height=".9rem"/></Box>
</HStack>
</VStack>
)

@ -1,5 +1,5 @@
import React from "react"
import { Box, Heading, HStack, Image, Text, useMediaQuery, VStack } from "@chakra-ui/react"
import { Box, Heading, HStack, Image, Tag, Text, useMediaQuery, VStack } from "@chakra-ui/react"
import { Story } from "src/types/story"
import StoryAuthor from "./story-author"
import Link from "next/link"
@ -31,11 +31,13 @@ export const StoryCard = (props: Props) => {
<a href={getStoryUrl(story)} target="_blank">
<Layout alignItems={isLargeScreen ? "top" : "left"} cursor="pointer" pl="2" pt="1">
<VStack alignItems="left" spacing={type === "classic" ? 3 : 2} width={isLargeScreen && type === "classic" ? "calc(100% - 18rem)" : '100%'}>
<Heading size="md" fontSize={type==="classic" ? '1.4rem' : '1.2rem'}><Highlighter
<Heading size="md" fontSize={type === "classic" ? '1.4rem' : '1.2rem'}>
<Highlighter
highlightClassName="highlight-search-match"
textToHighlight={story.title}
searchWords={[props.highlight]}
/>
{story.type === IDType.Series && <Tag size="sm" mt="1" ml="2">SERIES</Tag>}
</Heading>
{type !== "classic" && <HStack>{story.rawTags.map(t => <Text layerStyle="textSecondary" fontSize="md">#{t.name}</Text>)}</HStack>}
<Text layerStyle={type === "classic" ? "textSecondary" : null}>

@ -24,6 +24,9 @@ const customTheme = extendTheme({
styles: {
global: (props) => {
return ({
p: {
wordBreak: 'break-word'
},
'.hover-bg:hover': {
background: mode(userCustomTheme.hoverBg.light,userCustomTheme.hoverBg.dark )(props),
borderRadius: '6px'

@ -5,6 +5,7 @@ export default function markdownRender(props) {
console.log(props)
return {
'.markdown-render': {
wordBreak: 'break-word',
'.hljs' : {
padding: '1rem',
borderRadius: '8px'

Loading…
Cancel
Save