diff --git a/src/components/ArticleDigest/DropdownActions/index.tsx b/src/components/ArticleDigest/DropdownActions/index.tsx index 4fcb52b96d..9fc7749a90 100644 --- a/src/components/ArticleDigest/DropdownActions/index.tsx +++ b/src/components/ArticleDigest/DropdownActions/index.tsx @@ -1,3 +1,4 @@ +import classNames from 'classnames' import _isEmpty from 'lodash/isEmpty' import _pickBy from 'lodash/pickBy' import dynamic from 'next/dynamic' @@ -6,6 +7,7 @@ import { FormattedMessage, useIntl } from 'react-intl' import { ReactComponent as IconMore } from '@/public/static/icons/24px/more.svg' import { ERROR_CODES, ERROR_MESSAGES } from '~/common/enums' +import { capitalizeFirstLetter } from '~/common/utils' import { AddCollectionsArticleDialog, AddCollectionsArticleDialogProps, @@ -98,6 +100,7 @@ const DynamicArchiveUserDialog = dynamic( export interface DropdownActionsControls { icon?: React.ReactNode size?: IconSize + color?: 'greyDark' | 'black' sharePath?: string disabled?: boolean @@ -186,6 +189,7 @@ const BaseDropdownActions = ({ icon, size, + color = 'greyDark', inCard, disabled, @@ -222,35 +226,30 @@ const BaseDropdownActions = ({ openArchiveUserDialog, }: BaseDropdownActionsProps) => { const viewer = useContext(ViewerContext) - const hasPublic = hasShare || hasIPFS || hasExtend || hasReport - const hasPrivate = - hasSticky || - hasArchive || - hasSetTagSelected || - hasSetTagUnselected || - hasRemoveTag + + const isAuth = viewer.isAuthed + const isAuthor = viewer.id === article.author.id const Content = () => ( {/* public */} {hasShare && } - {hasIPFS && } {hasExtend && } - {hasReport && } - {/* private */} - {hasPublic && hasPrivate && } + {hasSticky && } + {hasBookmark && isAuth && ( + + )} + {hasIPFS && } + {hasReport && isAuth && !isAuthor && ( + + )} + {hasEdit && } {hasAddCollection && ( )} - {hasSticky && } - - {hasBookmark && ( - - )} - {hasSetTagSelected && tagDetailId && ( )} @@ -327,6 +326,11 @@ const BaseDropdownActions = ({ id: 'A7ugfn', }) + const moreButtonClasses = classNames({ + [styles.moreButton]: true, + [styles[`text${capitalizeFirstLetter(color)}`]]: !!color, + }) + return ( }> {({ openDropdown, ref }) => @@ -339,7 +343,7 @@ const BaseDropdownActions = ({ aria-label={moreActionText} aria-haspopup="listbox" ref={ref} - className={styles.moreButton} + className={moreButtonClasses} > {icon ? icon : } diff --git a/src/components/ArticleDigest/DropdownActions/styles.module.css b/src/components/ArticleDigest/DropdownActions/styles.module.css index b65bd4eef2..7c1449e407 100644 --- a/src/components/ArticleDigest/DropdownActions/styles.module.css +++ b/src/components/ArticleDigest/DropdownActions/styles.module.css @@ -1,11 +1,23 @@ .moreButton { @mixin transition; - color: var(--color-grey-dark); transition-property: color; +} + +.textGreyDark { + color: var(--color-grey-dark); &:hover, &:focus { color: var(--color-matters-green); } } + +.textBlack { + color: var(--color-black); + + &:hover, + &:focus { + color: var(--color-grey-darker); + } +} diff --git a/src/components/Buttons/Share/index.tsx b/src/components/Buttons/Share/index.tsx index 7caf1017dc..266279d24c 100644 --- a/src/components/Buttons/Share/index.tsx +++ b/src/components/Buttons/Share/index.tsx @@ -19,13 +19,16 @@ type ShareButtonBaseProps = { } & Omit type ShareButtonProps = ShareButtonBaseProps & - Pick + Pick< + ButtonProps, + 'bgColor' | 'size' | 'spacing' | 'disabled' | 'textActiveColor' + > export const ShareButton: React.FC< React.PropsWithChildren > = ({ children, - + textActiveColor, bgColor, hasIcon = true, iconSize, @@ -38,14 +41,6 @@ export const ShareButton: React.FC< }) => { const intl = useIntl() - const isGreen = bgColor === 'green' - const isHalfBlack = bgColor === 'halfBlack' - const buttonBgActiveColor = - isGreen || isHalfBlack - ? undefined - : inCard - ? 'greyLighterActive' - : 'greyLighter' const buttonSpacing = spacing || [8, 8] return ( @@ -54,8 +49,9 @@ export const ShareButton: React.FC< )} diff --git a/src/components/Context/ArticleAppreciation/index.tsx b/src/components/Context/ArticleAppreciation/index.tsx new file mode 100644 index 0000000000..735c32b981 --- /dev/null +++ b/src/components/Context/ArticleAppreciation/index.tsx @@ -0,0 +1,48 @@ +import { createContext, ReactNode, useState } from 'react' + +export const ArticleAppreciationContext = createContext( + {} as { + likesReceivedTotal: number + appreciateLeft: number + incrementLikesReceivedTotal: () => void + initArticleAppreciationContext: ( + likesReceivedTotal: number, + appreciateLeft: number + ) => void + } +) + +export const ArticleAppreciationProvider = ({ + children, +}: { + children: ReactNode +}) => { + const [likesReceivedTotal, setLikesReceivedTotal] = useState(0) + const [appreciateLeft, setAppreciateLeft] = useState(0) + + const initArticleAppreciationContext = ( + likesReceivedTotal: number, + appreciateLeft: number + ) => { + setLikesReceivedTotal(likesReceivedTotal) + setAppreciateLeft(appreciateLeft) + } + + const incrementLikesReceivedTotal = () => { + setLikesReceivedTotal((prev) => prev + 1) + setAppreciateLeft((prev) => prev - 1) + } + + return ( + + {children} + + ) +} diff --git a/src/components/Context/index.ts b/src/components/Context/index.ts index 352dedfc48..5228c0a9b4 100644 --- a/src/components/Context/index.ts +++ b/src/components/Context/index.ts @@ -1,4 +1,5 @@ export * from './ActiveCommentEditor' +export * from './ArticleAppreciation' export * from './CommentDrafts' export * from './DraftDetailState' export * from './Features' diff --git a/src/views/ArticleDetail/AppreciationButton/index.tsx b/src/views/ArticleDetail/AppreciationButton/index.tsx index 41a47178db..0f101c72c7 100644 --- a/src/views/ArticleDetail/AppreciationButton/index.tsx +++ b/src/views/ArticleDetail/AppreciationButton/index.tsx @@ -1,21 +1,16 @@ // import Script from 'next/script' -import { useContext, useEffect, useRef, useState } from 'react' +import { useContext, useRef, useState } from 'react' import { useDebouncedCallback } from 'use-debounce' -import { - APPRECIATE_DEBOUNCE, - EXTERNAL_LINKS, - SYNC_APPRECIATE_BUTTON_COUNT, - Z_INDEX, -} from '~/common/enums' +import { APPRECIATE_DEBOUNCE, EXTERNAL_LINKS, Z_INDEX } from '~/common/enums' import type { TurnstileInstance } from '~/components' import { + ArticleAppreciationContext, ButtonProps, ReCaptcha, toast, Tooltip, Translate, - useEventListener, useMutation, ViewerContext, } from '~/components' @@ -52,47 +47,25 @@ const AppreciationButton = ({ ...buttonProps }: AppreciationButtonProps) => { const viewer = useContext(ViewerContext) + const { likesReceivedTotal, appreciateLeft, incrementLikesReceivedTotal } = + useContext(ArticleAppreciationContext) const turnstileRef = useRef(null) - const [uuid, setUuid] = useState('') - - useEffect(() => { - setUuid(crypto.randomUUID()) - }, []) /** * Normal Appreciation */ const [amount, setAmount] = useState(0) - useEventListener( - SYNC_APPRECIATE_BUTTON_COUNT, - (detail: CustomEvent['detail']) => { - if (detail.uuid !== uuid) { - setAmount(detail.amount) - } - } - ) const [sendAppreciation] = useMutation(APPRECIATE_ARTICLE) const limit = article.appreciateLimit - const left = - (typeof article.appreciateLeft === 'number' - ? article.appreciateLeft - : limit) - amount - const total = article.likesReceivedTotal + amount + const left = appreciateLeft + const total = likesReceivedTotal const appreciatedCount = limit - left const isReachLimit = left <= 0 const debouncedSendAppreciation = useDebouncedCallback(async () => { try { - window.dispatchEvent( - new CustomEvent(SYNC_APPRECIATE_BUTTON_COUNT, { - detail: { - uuid, - amount, - }, - }) - ) await sendAppreciation({ variables: { id: article.id, @@ -187,6 +160,7 @@ const AppreciationButton = ({ sendSuperLike() } else { setAmount(amount + 1) + incrementLikesReceivedTotal() debouncedSendAppreciation() } } diff --git a/src/views/ArticleDetail/Toolbar/DesktopToolbar/index.tsx b/src/views/ArticleDetail/Toolbar/DesktopToolbar/index.tsx index 7aee7dc38e..d1073bf46a 100644 --- a/src/views/ArticleDetail/Toolbar/DesktopToolbar/index.tsx +++ b/src/views/ArticleDetail/Toolbar/DesktopToolbar/index.tsx @@ -37,10 +37,12 @@ const DesktopToolbar = ({ const dropdonwActionsProps: DropdownActionsControls = { inCard: true, hasEdit: true, + hasIPFS: true, hasBookmark: true, hasArchive: true, hasAddCollection: true, hasExtend: false, + color: 'black', ...props, } @@ -78,7 +80,7 @@ const DesktopToolbar = ({ { + initArticleAppreciationContext( + article.likesReceivedTotal, + article.appreciateLeft + ) + }, [article.appreciateLeft, article.likesReceivedTotal]) + // set language cookie for anonymous if it doesn't exist useEffect(() => { if (cookieLang || viewer.isAuthed || !routerLang) { @@ -595,9 +607,11 @@ const ArticleDetail = ({ * Render:Article */ return ( - - - + + + + + ) }