Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(article-settting): pin/unpin workflow #165

Merged
merged 1 commit into from
Oct 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/schemas/fragments/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import EMOTION from '@/constant/emotion'
import { titleCase } from '@/fmt'

export const community = `
id
title
slug
index
Expand Down
3 changes: 3 additions & 0 deletions src/spec/article.d.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import type { TColor } from './color'
import type { TCommunity, TTag } from '.'
import type { TUser, TAccount, TSimpleUser } from './account'
import type { TID } from './utils'
import type { TEmotion } from './emotion'

export type TArticleTitle = { isPinned?: boolean; viewerHasViewed?: boolean } & TColor

export type TCopyright = 'cc' | 'approve' | 'forbid'

export type TArticleMeta = {
Expand Down
1 change: 1 addition & 0 deletions src/spec/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ export type {
TArticleCatReject,
TArticleState,
TFAQSection,
TArticleTitle,
} from './article'

export type {
Expand Down
1 change: 0 additions & 1 deletion src/widgets/ArticleCatState/State.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { Trans } from '@/i18n'
import { aliasGTDDoneState } from '@/fmt'

import type { TProps as TArticleStateBadgeProps } from '.'

import { Wrapper, WipIcon, Text, TODOIcon, DoneIcon, RejectIcon } from './styles/state'

type TProps = Pick<TArticleStateBadgeProps, 'cat' | 'state' | 'smaller'>
Expand Down
6 changes: 5 additions & 1 deletion src/widgets/ArticlePinLabel/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import { FC, memo } from 'react'

import { buildLog } from '@/logger'
import usePrimaryColor from '@/hooks/usePrimaryColor'

import { PinIcon } from './styles'

/* eslint-disable-next-line */
Expand All @@ -20,7 +22,9 @@ export type TProps = {
}
}
const ArticlePinLabel: FC<TProps> = ({ article, top = 24, left = -30 }) => {
if (article.isPinned) return <PinIcon top={top} left={left} />
const primaryColor = usePrimaryColor()

if (article.isPinned) return <PinIcon top={top} left={left} $color={primaryColor} />

return null
}
Expand Down
8 changes: 4 additions & 4 deletions src/widgets/ArticlePinLabel/styles/index.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import styled from 'styled-components'

import css, { theme } from '@/css'
import type { TColor } from '@/spec'
import css, { rainbow } from '@/css'
import PinSVG from '@/icons/Pin'
import { pixelAdd } from '@/dom'

type TPos = { top: number; left: number }
type TPos = { top: number; left: number } & TColor

export const PinIcon = styled(PinSVG)<TPos>`
fill: ${theme('article.digest')};
fill: ${({ $color }) => rainbow($color)};
position: absolute;
${css.size(18)};
top: ${({ top }) => pixelAdd(`${top}px`, -4)};
left: ${({ left }) => `${left}px`};
opacity: 0.8;
transform: rotate(-30deg);
`

export const holder = 1
19 changes: 9 additions & 10 deletions src/widgets/ArticleReadLabel/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import { FC } from 'react'
import { observer } from 'mobx-react'

import type { TSpace } from '@/spec'
import { buildLog } from '@/logger'
import useAccount from '@/hooks/useAccount'

Expand All @@ -14,24 +15,22 @@ import { ReadedLabel } from './styles'
const log = buildLog('w:ArticleReadLabel:index')

export type TProps = {
top?: number
left?: number
article: {
viewerHasViewed?: boolean
isPinned?: boolean
}
}
const ArticleReadLabel: FC<TProps> = ({ article, top = 24, left = -30 }) => {
const accountInfo = useAccount()
const { isPinned, viewerHasViewed } = article
size?: number
} & TSpace

if (!accountInfo.isLogin || isPinned) return null
const ArticleReadLabel: FC<TProps> = ({ article, size = 8, ...restProps }) => {
const accountInfo = useAccount()
const { viewerHasViewed } = article

const { markViewed } = accountInfo.customization
if (!accountInfo.isLogin) return null

// return <ReadedLabel top={top} left={left} />
if (markViewed && !viewerHasViewed) {
return <ReadedLabel top={top} left={left} />
if (!viewerHasViewed) {
return <ReadedLabel size={size} {...restProps} />
}

return null
Expand Down
26 changes: 6 additions & 20 deletions src/widgets/ArticleReadLabel/styles/index.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,15 @@
import styled from 'styled-components'

import css, { theme } from '@/css'
import PinSVG from '@/icons/Pin'
import { pixelAdd } from '@/dom'
import { WithMargin } from '@/widgets/Common'

type TPos = { top: number; left: number }

export const ReadedLabel = styled.div<TPos>`
background: ${theme('article.info')};
${css.circle(8)};
position: absolute;
top: ${({ top }) => `${top}px`};
left: ${({ left }) => `${left}px`};
opacity: 0.5;
export const ReadedLabel = styled(WithMargin)<{ size: number }>`
${({ size }) => css.circle(size)};
background: ${theme('hint')};
opacity: 0.6;
${css.media.mobile`
${css.circle(6)};
`};
`
export const PinIcon = styled(PinSVG)<TPos>`
fill: ${theme('article.digest')};
position: absolute;
${css.size(18)};
top: ${({ top }) => pixelAdd(`${top}px`, -4)};
left: ${({ left }) => `${left}px`};
opacity: 0.8;
transform: rotate(-30deg);
`
export const holder = 1
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ type TProps = {
onClick: () => void
}

const CatMenuItem: FC<TProps> = ({ onClick }) => {
const CatItem: FC<TProps> = ({ onClick }) => {
const { article } = useViewingArticle()

if (article.cat) {
Expand All @@ -37,4 +37,4 @@ const CatMenuItem: FC<TProps> = ({ onClick }) => {
)
}

export default observer(CatMenuItem)
export default observer(CatItem)
50 changes: 50 additions & 0 deletions src/widgets/ArticleSettingMenu/Menu/PinItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { FC, useCallback, useEffect, useState } from 'react'
import { observer } from 'mobx-react'
import { useMutation } from 'urql'

import { toast, updateViewingArticle } from '@/signal'
import { SpaceGrow } from '@/widgets/Common'
import useViewingArticle from '@/hooks/useViewingArticle'

import S from '../schema'
import { Icon } from '../styles/icon'
import { MenuItem } from '../styles/menu'

const PinItem: FC = () => {
const { article } = useViewingArticle()
const [result, pinPost] = useMutation(S.pinPost)
const [result2, undoPinPost] = useMutation(S.undoPinPost)

const [pin, setPin] = useState(article.isPinned)

useEffect(() => {
setPin(article.isPinned)
}, [])

const handlePin = useCallback(() => {
const action = !pin
? pinPost({ id: article.id, communityId: article.originalCommunity.id })
: undoPinPost({ id: article.id, communityId: article.originalCommunity.id })

action.then((result) => {
if (result.error) {
toast('设置失败', 'error')
} else {
toast('设置完成')
setPin(!pin)
updateViewingArticle({ id: article.id, isPinned: !pin })
}
})
}, [pin, article, pinPost, undoPinPost])

return (
<MenuItem onClick={handlePin}>
{pin ? <Icon.UnPin /> : <Icon.Pin />}
{pin ? '取消置顶' : '置顶'}
<SpaceGrow />
{result.fetching && <Icon.Spin />}
</MenuItem>
)
}

export default observer(PinItem)
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ type TProps = {
onClick: () => void
}

const StateMenuItem: FC<TProps> = ({ onClick }) => {
const StateItem: FC<TProps> = ({ onClick }) => {
const { article } = useViewingArticle()
const bgColors = useKanbanBgColors()

Expand Down Expand Up @@ -44,4 +44,4 @@ const StateMenuItem: FC<TProps> = ({ onClick }) => {
)
}

export default observer(StateMenuItem)
export default observer(StateItem)
14 changes: 6 additions & 8 deletions src/widgets/ArticleSettingMenu/Menu/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ import type { TSubMenu } from '../spec'
import { SUB_MENU_TYPE } from '../constant'
import SubMenu from '../SubMenu'

import CatMenuItem from './CatMenuItem'
import StateMenuItem from './StateMenuItem'
import CatItem from './CatItem'
import PinItem from './PinItem'
import StateItem from './StateItem'

import { Icon } from '../styles/icon'
import { Wrapper, MenuItem, DangerMenuItem, ItemDivider } from '../styles/menu'
Expand Down Expand Up @@ -67,8 +68,8 @@ const Menu: FC<TProps> = ({ onSubMenuToggle, onClose }) => {
<Icon.Arrow />
</MenuItem>
<ItemDivider />
<CatMenuItem onClick={() => openSubMenu(SUB_MENU_TYPE.CATEGORY)} />
<StateMenuItem onClick={() => openSubMenu(SUB_MENU_TYPE.STATE)} />
<CatItem onClick={() => openSubMenu(SUB_MENU_TYPE.CATEGORY)} />
<StateItem onClick={() => openSubMenu(SUB_MENU_TYPE.STATE)} />
<MenuItem onClick={() => openSubMenu(SUB_MENU_TYPE.TAG)}>
<TagNode
opacity={0.5}
Expand All @@ -83,10 +84,7 @@ const Menu: FC<TProps> = ({ onSubMenuToggle, onClose }) => {
<Icon.Arrow />
</MenuItem>
<ItemDivider />
<MenuItem>
<Icon.Pin />
置顶
</MenuItem>
<PinItem />
<MenuItem>
<Icon.Lock />
关闭评论
Expand Down
19 changes: 19 additions & 0 deletions src/widgets/ArticleSettingMenu/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,29 @@ const setPostState = gql`
}
`

const pinPost = gql`
mutation ($id: ID!, $communityId: ID!) {
pinPost(id: $id, communityId: $communityId) {
id
}
}
`

const undoPinPost = gql`
mutation ($id: ID!, $communityId: ID!) {
undoPinPost(id: $id, communityId: $communityId) {
id
isPinned
}
}
`

const schema = {
updateTitle,
setPostCat,
setPostState,
pinPost,
undoPinPost,
}

export default schema
13 changes: 11 additions & 2 deletions src/widgets/ArticleSettingMenu/styles/icon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import styled from 'styled-components'

import type { TColor, TActive } from '@/spec'
import { ARTICLE_CAT, ARTICLE_STATE } from '@/constant/gtd'
import css, { rainbow, theme } from '@/css'
import css, { animate, rainbow, theme } from '@/css'
import PinSVG from '@/icons/Pin'
import UnPinSVG from '@/icons/UnPin'
//
import SpinSVG from '@/icons/Spin'
import EditSVG from '@/icons/EditPen'
import CategorySVG from '@/icons/Category'
import SlugSVG from '@/icons/Slug'
Expand Down Expand Up @@ -107,7 +109,14 @@ export const Icon = {
Pin: styled(commonIcon(PinSVG))`
margin-top: 2px;
`,

UnPin: styled(commonIcon(UnPinSVG))`
${css.size(15)};
margin-left: -1px;
`,
Spin: styled(commonIcon(SpinSVG))`
${css.size(11)};
animation: ${animate.rotate360} 0.8s linear infinite;
`,
[ARTICLE_CAT.FEATURE]: commonIcon(LightSVG),
[ARTICLE_CAT.QUESTION]: commonIcon(QuestionSVG),
[ARTICLE_CAT.BUG]: commonIcon(BugSVG),
Expand Down
11 changes: 11 additions & 0 deletions src/widgets/Icons/Spin.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { memo, SVGProps } from 'react'

const SVG = (props: SVGProps<SVGSVGElement>) => {
return (
<svg xmlns="http://www.w3.org/2000/svg" width={32} height={32} viewBox="0 0 256 256" {...props}>
<path d="M232 128a104 104 0 0 1-208 0c0-41 23.81-78.36 60.66-95.27a8 8 0 0 1 6.68 14.54C60.15 61.59 40 93.27 40 128a88 88 0 0 0 176 0c0-34.73-20.15-66.41-51.34-80.73a8 8 0 0 1 6.68-14.54C208.19 49.64 232 87 232 128Z" />
</svg>
)
}

export default memo(SVG)
9 changes: 9 additions & 0 deletions src/widgets/Icons/UnPin.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { FC } from 'react'

const SvgComponent: FC = (props) => (
<svg xmlns="http://www.w3.org/2000/svg" width={32} height={32} viewBox="0 0 256 256" {...props}>
<path d="M83.25 40a8 8 0 0 1 8-8H192a8 8 0 0 1 0 16h-6.46l18.75 106.3a8 8 0 0 1-6.48 9.26 7.52 7.52 0 0 1-1.4.13 8 8 0 0 1-7.87-6.61L169.29 48h-78a8 8 0 0 1-8.04-8Zm130.13 181.92a8 8 0 0 1-11.3-.54L168.1 184H136v56a8 8 0 0 1-16 0v-56H40a8 8 0 0 1 0-16h9.29l16.95-96-24.16-26.62a8 8 0 1 1 11.84-10.76l160 176a8 8 0 0 1-.54 11.3ZM153.55 168 79.84 86.92 65.54 168Z" />
</svg>
)

export default SvgComponent
14 changes: 13 additions & 1 deletion src/widgets/PostItem/QuoraLayout/DesktopView/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import TimeAgo from 'timeago-react'
import type { TPost } from '@/spec'
import useViewingCommunity from '@/hooks/useViewingCommunity'
import useBannerLayout from '@/hooks/useBannerLayout'
import usePrimaryColor from '@/hooks/usePrimaryColor'
import { THREAD } from '@/constant/thread'
import { BANNER_LAYOUT } from '@/constant/layout'
import SIZE from '@/constant/size'

import ArticleReadLabel from '@/widgets/ArticleReadLabel'
import Tooltip from '@/widgets/Tooltip'
import { SpaceGrow, Space } from '@/widgets/Common'
import TagsList from '@/widgets/TagsList'
Expand Down Expand Up @@ -37,6 +39,8 @@ type TProps = {
const Header: FC<TProps> = ({ article, onClick }) => {
const { slug } = useViewingCommunity()
const bannerLayout = useBannerLayout()
const primaryColor = usePrimaryColor()

const { author, title, commentsCount, innerId, articleTags, insertedAt } = article

return (
Expand All @@ -57,9 +61,17 @@ const Header: FC<TProps> = ({ article, onClick }) => {
</PublishTime>
</Topping>
<Main>
<Title onClick={(e) => e.preventDefault()} href={`/${slug}/${THREAD.POST}/${innerId}`}>
<Title
onClick={(e) => e.preventDefault()}
href={`/${slug}/${THREAD.POST}/${innerId}`}
isPinned={article.isPinned}
viewerHasViewed={article.viewerHasViewed}
$color={primaryColor}
>
<ArticleReadLabel article={article} right={8} top={1} size={7} />
{title}
</Title>

{/* @ts-ignore */}
<TagsList items={articleTags} left={12} top={2} />
<SpaceGrow />
Expand Down
Loading
Loading