diff --git a/lang/default.json b/lang/default.json index f6246138b9..2d10896945 100644 --- a/lang/default.json +++ b/lang/default.json @@ -121,10 +121,6 @@ "/usqHn": { "defaultMessage": "{displayName}'s creative space" }, - "/vyhs5": { - "defaultMessage": "Comment deleted", - "description": "src/components/Notice/NoticeComment.tsx" - }, "/wKyxw": { "defaultMessage": "Failed to republish" }, @@ -618,6 +614,10 @@ "defaultMessage": "Still quiet here. {br}Be the first one to say hello!", "description": "src/components/Empty/EmptyComment.tsx" }, + "7zn5ig": { + "defaultMessage": "Comment deleted", + "description": "src/components/Notice/NoticeComment.tsx/article" + }, "8+Z5E9": { "defaultMessage": "The badge signifies your participation and completion in the \"Free Write in 7 days\"." }, @@ -889,6 +889,10 @@ "defaultMessage": "Copy comment", "description": "src/components/Comment/DropdownActions/index.tsx" }, + "Ci7dxf": { + "defaultMessage": "Comment deleted", + "description": "src/components/Notice/NoticeComment.tsx/moment" + }, "CjKqYk": { "defaultMessage": "Share a story from your life" }, @@ -1486,9 +1490,6 @@ "N6PWfU": { "defaultMessage": "Forget Password" }, - "N8ISx8": { - "defaultMessage": "Oops! This comment has been deleted by author" - }, "NACY16": { "defaultMessage": "Why need to set up a wallet?", "description": "src/components/Forms/PaymentForm/BindWallet/index.tsx" @@ -3531,6 +3532,10 @@ "z3uIHQ": { "defaultMessage": "Undo upvote" }, + "z91BKe": { + "defaultMessage": "Archived Work", + "description": "src/components/Notice/NoticeArticleTitle.tsx" + }, "zAK5G+": { "defaultMessage": "The login link has been sent to {email}", "description": "src/components/Forms/Verification/LinkSent.tsx" diff --git a/lang/en.json b/lang/en.json index 6958c1ba9e..65438ff0f7 100644 --- a/lang/en.json +++ b/lang/en.json @@ -121,10 +121,6 @@ "/usqHn": { "defaultMessage": "{displayName}'s creative space" }, - "/vyhs5": { - "defaultMessage": "Comment deleted", - "description": "src/components/Notice/NoticeComment.tsx" - }, "/wKyxw": { "defaultMessage": "Failed to republish" }, @@ -618,6 +614,10 @@ "defaultMessage": "Still quiet here. {br}Be the first one to say hello!", "description": "src/components/Empty/EmptyComment.tsx" }, + "7zn5ig": { + "defaultMessage": "Comment deleted", + "description": "src/components/Notice/NoticeComment.tsx/article" + }, "8+Z5E9": { "defaultMessage": "The badge signifies your participation and completion in the \"Free Write in 7 days\"." }, @@ -889,6 +889,10 @@ "defaultMessage": "Copy comment", "description": "src/components/Comment/DropdownActions/index.tsx" }, + "Ci7dxf": { + "defaultMessage": "Comment deleted", + "description": "src/components/Notice/NoticeComment.tsx/moment" + }, "CjKqYk": { "defaultMessage": "Share a story from your life" }, @@ -1486,9 +1490,6 @@ "N6PWfU": { "defaultMessage": "Forget Password" }, - "N8ISx8": { - "defaultMessage": "Oops! This comment has been deleted by author" - }, "NACY16": { "defaultMessage": "Why need to set up a wallet?", "description": "src/components/Forms/PaymentForm/BindWallet/index.tsx" @@ -3531,6 +3532,10 @@ "z3uIHQ": { "defaultMessage": "Undo upvote" }, + "z91BKe": { + "defaultMessage": "Archived Work", + "description": "src/components/Notice/NoticeArticleTitle.tsx" + }, "zAK5G+": { "defaultMessage": "The login link has been sent to {email}", "description": "src/components/Forms/Verification/LinkSent.tsx" diff --git a/lang/zh-Hans.json b/lang/zh-Hans.json index 9987fe61cc..7bddb86514 100644 --- a/lang/zh-Hans.json +++ b/lang/zh-Hans.json @@ -121,10 +121,6 @@ "/usqHn": { "defaultMessage": "{displayName} 的创作空间站" }, - "/vyhs5": { - "defaultMessage": "留言已删除", - "description": "src/components/Notice/NoticeComment.tsx" - }, "/wKyxw": { "defaultMessage": "发布失败" }, @@ -618,6 +614,10 @@ "defaultMessage": "暂无评论", "description": "src/components/Empty/EmptyComment.tsx" }, + "7zn5ig": { + "defaultMessage": "评论已删除", + "description": "src/components/Notice/NoticeComment.tsx/article" + }, "8+Z5E9": { "defaultMessage": "纪念你参与「七日书」并完成七天书写" }, @@ -889,6 +889,10 @@ "defaultMessage": "复制留言", "description": "src/components/Comment/DropdownActions/index.tsx" }, + "Ci7dxf": { + "defaultMessage": "留言已删除", + "description": "src/components/Notice/NoticeComment.tsx/moment" + }, "CjKqYk": { "defaultMessage": "分享今天开心或难过的小故事吧" }, @@ -1486,9 +1490,6 @@ "N6PWfU": { "defaultMessage": "忘记密码" }, - "N8ISx8": { - "defaultMessage": "Oops!该评论已被原作者删除" - }, "NACY16": { "defaultMessage": "为什么需要设定钱包 ?", "description": "src/components/Forms/PaymentForm/BindWallet/index.tsx" @@ -3531,6 +3532,10 @@ "z3uIHQ": { "defaultMessage": "取消点赞" }, + "z91BKe": { + "defaultMessage": "已归档作品", + "description": "src/components/Notice/NoticeArticleTitle.tsx" + }, "zAK5G+": { "defaultMessage": "登录链接已发送至 {email}", "description": "src/components/Forms/Verification/LinkSent.tsx" diff --git a/lang/zh-Hant.json b/lang/zh-Hant.json index b50d7d0e83..c5bbdca94e 100644 --- a/lang/zh-Hant.json +++ b/lang/zh-Hant.json @@ -121,10 +121,6 @@ "/usqHn": { "defaultMessage": "{displayName} 的創作空間站" }, - "/vyhs5": { - "defaultMessage": "留言已刪除", - "description": "src/components/Notice/NoticeComment.tsx" - }, "/wKyxw": { "defaultMessage": "發布失敗" }, @@ -618,6 +614,10 @@ "defaultMessage": "暫無評論", "description": "src/components/Empty/EmptyComment.tsx" }, + "7zn5ig": { + "defaultMessage": "評論已刪除", + "description": "src/components/Notice/NoticeComment.tsx/article" + }, "8+Z5E9": { "defaultMessage": "紀念你參與「七日書」並完成七天書寫" }, @@ -889,6 +889,10 @@ "defaultMessage": "複製留言", "description": "src/components/Comment/DropdownActions/index.tsx" }, + "Ci7dxf": { + "defaultMessage": "留言已刪除", + "description": "src/components/Notice/NoticeComment.tsx/moment" + }, "CjKqYk": { "defaultMessage": "分享今天開心或難過的小故事吧" }, @@ -1486,9 +1490,6 @@ "N6PWfU": { "defaultMessage": "忘記密碼" }, - "N8ISx8": { - "defaultMessage": "Oops!該評論已被原作者刪除" - }, "NACY16": { "defaultMessage": "為什麼需要設定錢包 ?", "description": "src/components/Forms/PaymentForm/BindWallet/index.tsx" @@ -3531,6 +3532,10 @@ "z3uIHQ": { "defaultMessage": "取消點讚" }, + "z91BKe": { + "defaultMessage": "已封存作品", + "description": "src/components/Notice/NoticeArticleTitle.tsx" + }, "zAK5G+": { "defaultMessage": "登入連結已發送至 {email}", "description": "src/components/Forms/Verification/LinkSent.tsx" diff --git a/package.json b/package.json index 7e2a021cd7..4ee14b3f99 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matters-web", - "version": "5.6.1", + "version": "5.6.2", "description": "codebase of Matters' website", "author": "Matters ", "engines": { diff --git a/src/components/Comment/FooterActions/index.tsx b/src/components/Comment/FooterActions/index.tsx index bad961a5aa..27be351f3b 100644 --- a/src/components/Comment/FooterActions/index.tsx +++ b/src/components/Comment/FooterActions/index.tsx @@ -64,6 +64,7 @@ const fragments = { id isBlocking } + commentCount } ... on Moment { diff --git a/src/components/Dialogs/ArticleCommentFormDialog/CommentForm/index.tsx b/src/components/Dialogs/ArticleCommentFormDialog/CommentForm/index.tsx index db6e5a1a59..4ab5bca237 100644 --- a/src/components/Dialogs/ArticleCommentFormDialog/CommentForm/index.tsx +++ b/src/components/Dialogs/ArticleCommentFormDialog/CommentForm/index.tsx @@ -9,13 +9,11 @@ import { SpinnerBlock, useEventListener, useMutation, - useRoute, ViewerContext, } from '~/components' import { PUT_ARTICLE_COMMENT } from '~/components/GQL/mutations/putComment' import { updateArticleComments, - updateArticlePublic, updateCommentDetail, } from '~/components/GQL/updates' import { PutArticleCommentMutation } from '~/gql/graphql' @@ -54,8 +52,6 @@ const CommentForm: React.FC = ({ }) => { const viewer = useContext(ViewerContext) const formRef = useRef(null) - const { getQuery, routerLang } = useRoute() - const shortHash = getQuery('shortHash') const [putComment] = useMutation(PUT_ARTICLE_COMMENT) @@ -120,22 +116,6 @@ const CommentForm: React.FC = ({ comment: mutationResult.data?.putComment, }) } - - if (!!parentId) { - updateArticlePublic({ - cache, - shortHash, - routerLang, - type: 'addSecondaryComment', - }) - } else { - updateArticlePublic({ - cache, - shortHash, - routerLang, - type: 'addComment', - }) - } }, }) diff --git a/src/components/Dialogs/MomentDetailDialog/Content.tsx b/src/components/Dialogs/MomentDetailDialog/Content.tsx index 95f049a879..f831c10864 100644 --- a/src/components/Dialogs/MomentDetailDialog/Content.tsx +++ b/src/components/Dialogs/MomentDetailDialog/Content.tsx @@ -2,7 +2,6 @@ import { useQuery } from '@apollo/react-hooks' import { Editor } from '@matters/matters-editor' import classNames from 'classnames' import { useEffect, useState } from 'react' -import { FormattedMessage } from 'react-intl' import { ADD_MOMENT_COMMENT_MENTION, @@ -95,24 +94,20 @@ const MomentDetailDialogContent = ({ } if (error) { - return - } - - if (data?.moment?.__typename !== 'Moment') { - return null + return ( +
+ +
+ ) } - if (data.moment.state === MomentState.Archived) { + if ( + data?.moment?.__typename !== 'Moment' || + data.moment.state === MomentState.Archived + ) { return (
- - } - > +
diff --git a/src/components/Drawer/styles.module.css b/src/components/Drawer/styles.module.css index 06d3c536e6..1f130b6b60 100644 --- a/src/components/Drawer/styles.module.css +++ b/src/components/Drawer/styles.module.css @@ -17,6 +17,7 @@ flex-direction: column; padding: 0 var(--sp48); overflow-y: auto; + overscroll-behavior: contain; visibility: hidden; background: white; box-shadow: -8px 0 56px 0 rgb(0 0 0 / 8%) !important; diff --git a/src/components/Editor/Comment/index.tsx b/src/components/Editor/Comment/index.tsx index 553e77f426..36a0dc5bf7 100644 --- a/src/components/Editor/Comment/index.tsx +++ b/src/components/Editor/Comment/index.tsx @@ -30,6 +30,7 @@ interface Props { onFocused?: () => void isFallbackEditor?: boolean lockScroll?: boolean + editable?: boolean } const CommentEditor: React.FC = ({ @@ -41,6 +42,7 @@ const CommentEditor: React.FC = ({ onFocused, isFallbackEditor, lockScroll = true, + editable = true, }) => { const client = useApolloClient() const intl = useIntl() @@ -54,6 +56,7 @@ const CommentEditor: React.FC = ({ }) const editor = useEditor({ + editable, content: content || '', onUpdate: async ({ editor, transaction }) => { const content = editor.getHTML() diff --git a/src/components/Editor/SetCover/Uploader.tsx b/src/components/Editor/SetCover/Uploader.tsx index bc93bd0f8e..0015e10686 100644 --- a/src/components/Editor/SetCover/Uploader.tsx +++ b/src/components/Editor/SetCover/Uploader.tsx @@ -10,7 +10,7 @@ import { ASSET_TYPE, ENTITY_TYPE, } from '~/common/enums' -import { sleep, validateImage } from '~/common/utils' +import { validateImage } from '~/common/utils' import { DraftDetailStateContext, Icon, @@ -55,13 +55,14 @@ const Uploader: React.FC = ({ const [upload, { loading }] = useMutation( DIRECT_IMAGE_UPLOAD, + undefined, + { showToast: false } + ) + const [directImageUploadDone] = useMutation( + DIRECT_IMAGE_UPLOAD_DONE, { update: async (cache, { data }) => { if (data?.directImageUpload) { - // FIXME: newly uploaded images will return 404 in a short time - // https://community.cloudflare.com/t/new-uploaded-images-need-about-10-min-to-display-in-my-website/121568 - await sleep(300) - updateDraftAssets({ cache, id: entityId, @@ -72,11 +73,6 @@ const Uploader: React.FC = ({ }, { showToast: false } ) - const [directImageUploadDone] = useMutation( - DIRECT_IMAGE_UPLOAD_DONE, - undefined, - { showToast: false } - ) const { upload: uploadImage, uploading } = useDirectImageUpload() const { isInPath } = useRoute() diff --git a/src/components/Error/index.tsx b/src/components/Error/index.tsx index 59bf7a4977..6c7e2b67f4 100644 --- a/src/components/Error/index.tsx +++ b/src/components/Error/index.tsx @@ -1,8 +1,8 @@ import { Alert } from '@reach/alert' import { useContext } from 'react' -import IMAGE_ILLUSTRATION_EMPTY from '@/public/static/images/illustration-empty.svg' -import { LanguageContext } from '~/components' +import { ReactComponent as IconIllustrationEmpty } from '@/public/static/images/illustration-empty.svg' +import { Icon, LanguageContext } from '~/components' import { UserLanguage } from '~/gql/graphql' import styles from './styles.module.css' @@ -62,7 +62,13 @@ export const Error: React.FC> = ({ aria-atomic="true" >
- illustration +
diff --git a/src/components/Forms/ArticleCommentForm/index.tsx b/src/components/Forms/ArticleCommentForm/index.tsx index 9e9f7bf2a6..614acafce0 100644 --- a/src/components/Forms/ArticleCommentForm/index.tsx +++ b/src/components/Forms/ArticleCommentForm/index.tsx @@ -15,15 +15,10 @@ import { useCommentEditorContext, useEventListener, useMutation, - useRoute, ViewerContext, } from '~/components' import CommentEditor from '~/components/Editor/Comment' -import { - updateArticleComments, - updateArticlePublic, - updateCommentDetail, -} from '~/components/GQL' +import { updateArticleComments, updateCommentDetail } from '~/components/GQL' import { PUT_ARTICLE_COMMENT } from '~/components/GQL/mutations/putComment' import { PutArticleCommentMutation } from '~/gql/graphql' @@ -63,14 +58,12 @@ export const ArticleCommentForm: React.FC = ({ }) => { const intl = useIntl() const viewer = useContext(ViewerContext) - const { getQuery, routerLang } = useRoute() const { setActiveEditor } = useCommentEditorContext() const [editor, localSetEditor] = useState(null) const setEditor = (editor: Editor | null) => { localSetEditor(editor) propsSetEditor?.(editor) } - const shortHash = getQuery('shortHash') const [putComment] = useMutation(PUT_ARTICLE_COMMENT) @@ -136,22 +129,6 @@ export const ArticleCommentForm: React.FC = ({ comment: mutationResult.data?.putComment, }) } - - if (!!parentId) { - updateArticlePublic({ - cache, - shortHash, - routerLang, - type: 'addSecondaryComment', - }) - } else { - updateArticlePublic({ - cache, - shortHash, - routerLang, - type: 'addComment', - }) - } }, }) @@ -211,6 +188,17 @@ export const ArticleCommentForm: React.FC = ({ id: 'bTNYGv', description: 'src/components/Forms/ArticleCommentForm/index.tsx', })} + onClick={(event) => { + if (!viewer.isAuthed) { + event.preventDefault() + window.dispatchEvent( + new CustomEvent(OPEN_UNIVERSAL_AUTH_DIALOG, { + detail: { trigger: UNIVERSAL_AUTH_TRIGGER.collectArticle }, + }) + ) + return + } + }} >
= ({ setEditor={(editor) => { setEditor(editor) }} + editable={viewer.isAuthed} />
diff --git a/src/components/GQL/updates/articlePublic.ts b/src/components/GQL/updates/articlePublic.ts index f34e933746..54da6e07c7 100644 --- a/src/components/GQL/updates/articlePublic.ts +++ b/src/components/GQL/updates/articlePublic.ts @@ -22,12 +22,7 @@ export const updateArticlePublic = ({ routerLang: UserLanguage viewer?: Viewer txId?: string - type: - | 'deleteComment' - | 'addComment' - | 'addSecondaryComment' - | 'deleteSecondaryComment' - | 'updateDonation' + type: 'deleteComment' | 'deleteSecondaryComment' | 'updateDonation' }) => { // FIXME: circular dependencies const { @@ -68,17 +63,10 @@ export const updateArticlePublic = ({ let commentCount = data.article.commentCount let totalCount = data.article.comments.totalCount switch (type) { - case 'addComment': - totalCount += 1 - commentCount += 1 - break case 'deleteComment': totalCount -= 1 commentCount -= 1 break - case 'addSecondaryComment': - commentCount += 1 - break case 'deleteSecondaryComment': commentCount -= 1 break diff --git a/src/components/Icon/withIcon.tsx b/src/components/Icon/withIcon.tsx index 5b377b884d..03b8c68214 100644 --- a/src/components/Icon/withIcon.tsx +++ b/src/components/Icon/withIcon.tsx @@ -16,6 +16,7 @@ export type IconSize = | 48 | 64 | 88 + | 240 export type IconColor = | 'white' diff --git a/src/components/Layout/NavBar/NavBanner.tsx b/src/components/Layout/NavBar/MomentNavBanner.tsx similarity index 72% rename from src/components/Layout/NavBar/NavBanner.tsx rename to src/components/Layout/NavBar/MomentNavBanner.tsx index e90dae2767..e208edd733 100644 --- a/src/components/Layout/NavBar/NavBanner.tsx +++ b/src/components/Layout/NavBar/MomentNavBanner.tsx @@ -5,9 +5,13 @@ import MATTY from '@/public/static/images/matty.png' import styles from './styles.module.css' -const NavBanner: React.FC = () => { +type MomentNavBannerProps = { + onClick: () => void +} + +const MomentNavBanner: React.FC = ({ onClick }) => { return ( -
+
@@ -22,4 +26,4 @@ const NavBanner: React.FC = () => { ) } -export default NavBanner +export default MomentNavBanner diff --git a/src/components/Layout/NavBar/NavCreate.tsx b/src/components/Layout/NavBar/NavCreate.tsx index f3226b00ea..8171db38ab 100644 --- a/src/components/Layout/NavBar/NavCreate.tsx +++ b/src/components/Layout/NavBar/NavCreate.tsx @@ -19,7 +19,7 @@ import { } from '~/components' import SideNavNavListItem from '../SideNav/NavListItem' -import NavBanner from './NavBanner' +import MomentNavBanner from './MomentNavBanner' import NavPopover from './NavPopover' export const NavCreate = () => { @@ -47,11 +47,19 @@ export const NavCreate = () => { arrow={true} onHidden={closeMomentBanner} visible={showMomentBanner} - content={} + content={ + { + closeMomentBanner() + openWriteDropdown() + }} + /> + } placement="top" onShown={hidePopperOnClick} offset={[0, 12]} // 16px - 4px (default tippy padding) theme="banner" + appendTo="parent" > {({ ref: bannerRef }) => ( { onShown={hidePopperOnClick} offset={[0, 12]} // 16px - 4px (default tippy padding) theme="mobile" + appendTo="parent" > {({ ref: navRef }) => ( diff --git a/src/components/Notice/NoticeArticleTitle.tsx b/src/components/Notice/NoticeArticleTitle.tsx index 4206eba4f3..4c7b1934c6 100644 --- a/src/components/Notice/NoticeArticleTitle.tsx +++ b/src/components/Notice/NoticeArticleTitle.tsx @@ -1,10 +1,11 @@ import gql from 'graphql-tag' import Link from 'next/link' +import { FormattedMessage } from 'react-intl' import { TEST_ID } from '~/common/enums' import { toPath } from '~/common/utils' import { ArticleDigestTitle } from '~/components/ArticleDigest' -import { NoticeArticleTitleFragment } from '~/gql/graphql' +import { ArticleState, NoticeArticleTitleFragment } from '~/gql/graphql' import styles from './styles.module.css' @@ -24,6 +25,8 @@ const NoticeArticleTitle = ({ article, }) + const isArchived = article.articleState === ArticleState.Archived + if (!isBlock) { return ( @@ -31,7 +34,15 @@ const NoticeArticleTitle = ({ className={styles.noticeArticleTitle} data-test-id={TEST_ID.NOTICE_ARTICLE_TITLE} > - {article.title} + {isArchived ? ( + + ) : ( + article.title + )} ) diff --git a/src/components/Notice/NoticeComment.tsx b/src/components/Notice/NoticeComment.tsx index 43674952af..f17c6ea196 100644 --- a/src/components/Notice/NoticeComment.tsx +++ b/src/components/Notice/NoticeComment.tsx @@ -19,6 +19,7 @@ const fragments = { id title slug + articleState: state shortHash author { id @@ -31,7 +32,7 @@ const fragments = { } ... on Moment { id - state + momentState: state shortHash } } @@ -98,8 +99,8 @@ const NoticeComment = ({ @@ -107,28 +108,26 @@ const NoticeComment = ({ ) } - if (comment.state === 'archived') { + if (comment.state === 'archived' && article) { return ( - +
+ +
) } - if (comment.state === 'active' && moment && moment.state === 'archived') { + if ( + comment.state === 'active' && + ((moment && moment.momentState === 'archived') || + (article && article.articleState === 'archived')) + ) { return (
diff --git a/src/components/SquareTabs/index.tsx b/src/components/SquareTabs/index.tsx index b47b2b9126..886768f992 100644 --- a/src/components/SquareTabs/index.tsx +++ b/src/components/SquareTabs/index.tsx @@ -1,4 +1,5 @@ import classNames from 'classnames' +import { useEffect, useRef, useState } from 'react' import styles from './styles.module.css' @@ -34,15 +35,89 @@ interface SquareTabsProps { export const SquareTabs: React.FC> & { Tab: typeof Tab } = ({ children, sticky }) => { + const navRef = useRef(null) + const containerRef = useRef(null) + const $nav = navRef.current + const $container = containerRef.current + const [showLeftGradient, setShowLeftGradient] = useState(false) + const [showRightGradient, setShowRightGradient] = useState(false) + const [isDragging, setIsDragging] = useState(false) + const [startX, setStartX] = useState(0) + const [scrollLeft, setScrollLeft] = useState(0) + + const isTabsOverflowing = () => { + if (!$nav || !$container) return false + return $nav.scrollWidth > $container.clientWidth + } + + const calculateGradient = () => { + if (!$nav || !$container) return + + const isAtLeftMost = $nav.scrollLeft <= 0 + const isAtRightMost = $nav.scrollLeft + $nav.clientWidth >= $nav.scrollWidth + + setShowLeftGradient(!isAtLeftMost) + setShowRightGradient(!isAtRightMost) + } + + const handleMouseDown = (e: React.MouseEvent) => { + if (!$nav) return + setIsDragging(true) + setStartX(e.pageX - $nav.offsetLeft) + setScrollLeft($nav.scrollLeft) + } + + const handleMouseMove = (e: MouseEvent) => { + if (!isDragging || !$nav) return + const x = e.pageX - $nav.offsetLeft + const walk = (x - startX) * 2 // scroll-fast + $nav.scrollLeft = scrollLeft - walk + calculateGradient() + } + + const handleMouseUp = () => { + setIsDragging(false) + } + + useEffect(() => { + if (!isTabsOverflowing() || !$nav) return + + // initial gradient + calculateGradient() + + $nav.addEventListener('scroll', calculateGradient) + $nav.addEventListener('mousemove', handleMouseMove) + $nav.addEventListener('mouseup', handleMouseUp) + + return () => { + $nav.removeEventListener('scroll', calculateGradient) + $nav.removeEventListener('mousemove', handleMouseMove) + $nav.removeEventListener('mouseup', handleMouseUp) + } + }, [$nav, $container, isDragging]) + + const containerClasses = classNames({ + [styles.container]: true, + [styles.showLeftGradient]: showLeftGradient, + [styles.showRightGradient]: showRightGradient, + }) + const navClasses = classNames({ [styles.tabList]: true, [styles.sticky]: sticky, }) return ( -
    - {children} -
+
+
    + {children} +
+
) } diff --git a/src/components/SquareTabs/styles.module.css b/src/components/SquareTabs/styles.module.css index c98570d7b3..b20d95eb29 100644 --- a/src/components/SquareTabs/styles.module.css +++ b/src/components/SquareTabs/styles.module.css @@ -1,8 +1,49 @@ +.container { + position: relative; + + &::before, + &::after { + position: absolute; + top: 0; + bottom: 0; + z-index: calc(var(--z-index-sticky-tabs) + 1); + width: 7.5rem; + pointer-events: none; + content: ''; + opacity: 0; /* Initially hidden */ + transition: opacity 0.3s; + } + + &.showLeftGradient::before, + &.showRightGradient::after { + opacity: 1; /* Show when scrollable */ + } + + &::before { + left: 0; + background: linear-gradient( + -90deg, + rgb(255 255 255 / 0%) 0%, + rgb(255 255 255 / 30%) 20%, + #fff 100% + ); + } + + &::after { + right: 0; + background: linear-gradient( + 90deg, + rgb(255 255 255 / 0%) 0%, + rgb(255 255 255 / 30%) 20%, + #fff 100% + ); + } +} + .tabList { @mixin hide-scrollbar; display: flex; - gap: var(--sp16); overflow-x: auto; -webkit-overflow-scrolling: touch; @@ -19,10 +60,12 @@ flex-shrink: 0; padding: var(--sp5) var(--sp10); + margin-right: var(--sp16); font-size: var(--text14); line-height: 1.375rem; color: var(--color-grey-darker); cursor: pointer; + user-select: none; background: var(--color-grey-lighter); border-radius: 0.5rem; transition-property: background-color, color; diff --git a/src/stories/components/ActivityPopover/ActivityPopover.stories.tsx b/src/stories/components/ActivityPopover/ActivityPopover.stories.tsx index 22f581f038..623ef1db06 100644 --- a/src/stories/components/ActivityPopover/ActivityPopover.stories.tsx +++ b/src/stories/components/ActivityPopover/ActivityPopover.stories.tsx @@ -10,7 +10,7 @@ import { Icon, useDialogSwitch, } from '~/components' -import NavBanner from '~/components/Layout/NavBar/NavBanner' +import MomentNavBanner from '~/components/Layout/NavBar/MomentNavBanner' import NavPopover from '~/components/Layout/NavBar/NavPopover' import Activity from '~/components/Layout/SideNav/Activity' import NavListItem from '~/components/Layout/SideNav/NavListItem' @@ -94,7 +94,7 @@ export const ActivityBanner: StoryFn = () => { - + {}} />
} visible={show} diff --git a/src/views/ArticleDetail/Edit/index.tsx b/src/views/ArticleDetail/Edit/index.tsx index 08f42efdbc..1b88e284ab 100644 --- a/src/views/ArticleDetail/Edit/index.tsx +++ b/src/views/ArticleDetail/Edit/index.tsx @@ -1,7 +1,7 @@ import { useQuery } from '@apollo/react-hooks' import _omit from 'lodash/omit' import dynamic from 'next/dynamic' -import { useContext, useState } from 'react' +import { useContext, useEffect, useState } from 'react' import { ASSET_TYPE, @@ -86,6 +86,10 @@ const BaseEdit = ({ article }: { article: Article }) => { // cover const [assets, setAssets] = useState(article.assets || []) + + useEffect(() => { + setAssets(article.assets || []) + }, [article.assets]) const [cover, setCover] = useState( assets.find((asset) => asset.path === article.cover) ) diff --git a/src/views/ArticleDetail/index.tsx b/src/views/ArticleDetail/index.tsx index 83eed8e366..17fba58490 100644 --- a/src/views/ArticleDetail/index.tsx +++ b/src/views/ArticleDetail/index.tsx @@ -404,9 +404,11 @@ const BaseArticleDetail = ({ )} - + + + {article.comments.totalCount > 0 && (
@@ -580,18 +582,14 @@ const ArticleDetail = ({ - ) : article.state === 'banned' ? ( + article.state === 'banned' ? ( ) : null } + type="not_found" > diff --git a/src/views/CampaignDetail/index.tsx b/src/views/CampaignDetail/index.tsx index a10039114a..9248a45153 100644 --- a/src/views/CampaignDetail/index.tsx +++ b/src/views/CampaignDetail/index.tsx @@ -1,4 +1,5 @@ import { useQuery } from '@apollo/react-hooks' +import dynamic from 'next/dynamic' import { useContext } from 'react' import { toPath } from '~/common/utils' @@ -17,7 +18,11 @@ import { CampaignDetailQuery } from '~/gql/graphql' import ArticleFeeds from './ArticleFeeds' import { CAMPAIGN_DETAIL } from './gql' import InfoHeader from './InfoHeader' -import SideParticipants from './SideParticipants' + +const DynamicSideParticipants = dynamic(() => import('./SideParticipants'), { + loading: () => , + ssr: false, +}) const CampaignDetail = () => { const { lang } = useContext(LanguageContext) @@ -58,7 +63,7 @@ const CampaignDetail = () => { const path = toPath({ page: 'campaignDetail', campaign }) return ( - }> + }> { const { data } = await putMoment({ variables: { input: { - content, + content: sanitizeContent(content), assets: assets.map(({ assetId }) => assetId), }, },