From 5fda3b8d970ce4925e91bd41adab3bc98abc57b4 Mon Sep 17 00:00:00 2001 From: bluecloud <96812901+pitb2022@users.noreply.github.com> Date: Tue, 9 Jan 2024 18:00:21 +0800 Subject: [PATCH] feat(AuthorSidebar): add Collection --- src/common/enums/test.ts | 1 + src/common/utils/analytics.ts | 1 + .../AuthorSidebar/AuthorSidebar.test.tsx | 18 ++++ .../AuthorSidebar/Placeholder/index.tsx | 15 +++ .../Placeholder/styles.module.css | 24 +++++ .../ArticleDigest/AuthorSidebar/index.tsx | 91 +++++++++++++++++++ .../AuthorSidebar/styles.module.css | 47 ++++++++++ src/components/ArticleDigest/index.tsx | 1 + .../AuthorSidebar/Author/styles.module.css | 7 +- .../AuthorSidebar/Collection/gql.ts | 7 +- .../AuthorSidebar/Collection/index.tsx | 67 ++++++++++++-- .../Collection/styles.module.css | 10 ++ .../AuthorSidebar/Placeholder/index.tsx | 12 +++ 13 files changed, 289 insertions(+), 12 deletions(-) create mode 100644 src/components/ArticleDigest/AuthorSidebar/AuthorSidebar.test.tsx create mode 100644 src/components/ArticleDigest/AuthorSidebar/Placeholder/index.tsx create mode 100644 src/components/ArticleDigest/AuthorSidebar/Placeholder/styles.module.css create mode 100644 src/components/ArticleDigest/AuthorSidebar/index.tsx create mode 100644 src/components/ArticleDigest/AuthorSidebar/styles.module.css diff --git a/src/common/enums/test.ts b/src/common/enums/test.ts index 9779d3f932..5d819ea07a 100644 --- a/src/common/enums/test.ts +++ b/src/common/enums/test.ts @@ -15,6 +15,7 @@ export enum TEST_ID { DIGEST_ARTICLE_FEED_FOOTER_PIN = 'digest/article/feed/footer/pin', DIGEST_ARTICLE_NOTICE = 'digest/article/notice', DIGEST_ARTICLE_SIDEBAR = 'digest/article/sidebar', + DIGEST_ARTICLE_AUTHOR_SIDEBAR = 'digest/article/author-sidebar', DIGEST_ARTICLE_TITLE = 'digest/article/title', DIGEST_ARTICLE_PUBLISHED = 'digest/article/published', DIGEST_ARTICLE_PUBLISHED_READER_COUNT = 'digest/article/published/reader-count', diff --git a/src/common/utils/analytics.ts b/src/common/utils/analytics.ts index 35da3c941f..b618565de8 100644 --- a/src/common/utils/analytics.ts +++ b/src/common/utils/analytics.ts @@ -249,6 +249,7 @@ type ArticleFeedType = | 'wallet' | 'related_donations' | 'circle_detail' + | 'article_detail_author-sidebar-collection' type CollectionFeedType = | 'user_collection' diff --git a/src/components/ArticleDigest/AuthorSidebar/AuthorSidebar.test.tsx b/src/components/ArticleDigest/AuthorSidebar/AuthorSidebar.test.tsx new file mode 100644 index 0000000000..a2a6121c8f --- /dev/null +++ b/src/components/ArticleDigest/AuthorSidebar/AuthorSidebar.test.tsx @@ -0,0 +1,18 @@ +import { describe, expect, it } from 'vitest' + +import { TEST_ID } from '~/common/enums' +import { render, screen } from '~/common/utils/test' +import { ArticleDigestAuthorSidebar } from '~/components' +import { MOCK_ARTILCE } from '~/stories/mocks' + +describe('', () => { + it('should render an ArticleDigest.Sidebar', () => { + render() + + const $digest = screen.getByTestId(TEST_ID.DIGEST_ARTICLE_AUTHOR_SIDEBAR) + expect($digest).toBeInTheDocument() + + const $title = screen.getByRole('heading', { name: MOCK_ARTILCE.title }) + expect($title).toBeInTheDocument() + }) +}) diff --git a/src/components/ArticleDigest/AuthorSidebar/Placeholder/index.tsx b/src/components/ArticleDigest/AuthorSidebar/Placeholder/index.tsx new file mode 100644 index 0000000000..12c722cdc0 --- /dev/null +++ b/src/components/ArticleDigest/AuthorSidebar/Placeholder/index.tsx @@ -0,0 +1,15 @@ +import styles from './styles.module.css' + +const Placeholder = () => { + return ( +
+
+
+
+
+
+
+ ) +} + +export default Placeholder diff --git a/src/components/ArticleDigest/AuthorSidebar/Placeholder/styles.module.css b/src/components/ArticleDigest/AuthorSidebar/Placeholder/styles.module.css new file mode 100644 index 0000000000..884bd126af --- /dev/null +++ b/src/components/ArticleDigest/AuthorSidebar/Placeholder/styles.module.css @@ -0,0 +1,24 @@ +.container { + @mixin flex-start-space-between; + + margin: var(--spacing-tight) 0; + + & .title { + display: flex; + flex-direction: column; + gap: var(--spacing-base-tight); + + & .text { + width: 9.375rem; + height: 1rem; + background-color: var(--color-grey-lighter); + } + } + + & .image { + width: 2.75rem; + height: 2.75rem; + background-color: var(--color-grey-lighter); + border-radius: 0.25rem; + } +} diff --git a/src/components/ArticleDigest/AuthorSidebar/index.tsx b/src/components/ArticleDigest/AuthorSidebar/index.tsx new file mode 100644 index 0000000000..6d637b6509 --- /dev/null +++ b/src/components/ArticleDigest/AuthorSidebar/index.tsx @@ -0,0 +1,91 @@ +import classNames from 'classnames' +import gql from 'graphql-tag' + +import { TEST_ID } from '~/common/enums' +import { capitalizeFirstLetter, toPath } from '~/common/utils' +import { LinkWrapper, ResponsiveImage } from '~/components' +import { ArticleDigestAuthorSidebarArticleFragment } from '~/gql/graphql' + +import { ArticleDigestTitle, ArticleDigestTitleTextSize } from '../Title' +import Placeholder from './Placeholder' +import styles from './styles.module.css' + +export type ArticleDigestAuthorSidebarProps = { + article: ArticleDigestAuthorSidebarArticleFragment + collectionId?: string + titleTextSize?: ArticleDigestTitleTextSize + titleColor?: 'greyDarker' | 'black' +} + +const fragments = { + article: gql` + fragment ArticleDigestAuthorSidebarArticle on Article { + id + articleState: state + title + slug + mediaHash + cover + ...ArticleDigestTitleArticle + } + ${ArticleDigestTitle.fragments.article} + `, +} + +export const ArticleDigestAuthorSidebar = ({ + article, + collectionId, + + titleTextSize = 'mdS', + titleColor = 'greyDarker', +}: ArticleDigestAuthorSidebarProps) => { + const { articleState: state } = article + const isBanned = state === 'banned' + const cover = !isBanned ? article.cover : null + const containerClasses = classNames({ + [styles.container]: true, + [styles.hasCover]: !!cover, + }) + const path = toPath({ + page: 'articleDetail', + article, + collectionId, + }) + + const headerClasses = classNames({ + [styles[`textColor${capitalizeFirstLetter(titleColor)}`]]: !!titleColor, + }) + + return ( +
+
+ +
+ + {cover && ( + + + + )} +
+ ) +} + +ArticleDigestAuthorSidebar.Placeholder = Placeholder +ArticleDigestAuthorSidebar.fragments = fragments diff --git a/src/components/ArticleDigest/AuthorSidebar/styles.module.css b/src/components/ArticleDigest/AuthorSidebar/styles.module.css new file mode 100644 index 0000000000..3981896d05 --- /dev/null +++ b/src/components/ArticleDigest/AuthorSidebar/styles.module.css @@ -0,0 +1,47 @@ +.container { + margin-top: var(--spacing-tight); + margin-bottom: var(--spacing-tight); + + @mixin flex-start-space-between; + + & header { + flex-grow: 1; + + & h3 { + line-height: 1.375rem; + } + + &.textColorBlack { + & a { + color: var(--color-black); + } + } + + &.textColorGreyDarker { + & a { + color: var(--color-grey-darker); + } + } + } + + &.hasCover { + position: relative; + min-height: 2.75rem; + padding-right: calc(2.75rem + var(--spacing-loose)); + + & .cover { + position: absolute; + top: 0; + right: 0; + display: initial; + width: 2.75rem; + height: 2.75rem; + + & img { + @mixin object-fit-cover; + + border-radius: var(--spacing-xx-tight); + } + } + } +} diff --git a/src/components/ArticleDigest/index.tsx b/src/components/ArticleDigest/index.tsx index 127586ffeb..5f27555c9d 100644 --- a/src/components/ArticleDigest/index.tsx +++ b/src/components/ArticleDigest/index.tsx @@ -1,4 +1,5 @@ export * from './Archived' +export * from './AuthorSidebar' export * from './Card' export * from './Dropdown' export * from './Feed' diff --git a/src/views/ArticleDetail/AuthorSidebar/Author/styles.module.css b/src/views/ArticleDetail/AuthorSidebar/Author/styles.module.css index d8d7066648..1a2acd195d 100644 --- a/src/views/ArticleDetail/AuthorSidebar/Author/styles.module.css +++ b/src/views/ArticleDetail/AuthorSidebar/Author/styles.module.css @@ -1,14 +1,19 @@ .container { display: flex; gap: var(--spacing-loose); - align-items: center; + align-items: start; margin-top: var(--spacing-x-loose); + & > a { + align-self: center; + } + & .info { @mixin flex-start-center; flex-direction: column; gap: var(--spacing-xx-tight); + color: var(--color-black); & .displayName { font-size: var(--font-size-sm); diff --git a/src/views/ArticleDetail/AuthorSidebar/Collection/gql.ts b/src/views/ArticleDetail/AuthorSidebar/Collection/gql.ts index 32d7257d53..3ab888e748 100644 --- a/src/views/ArticleDetail/AuthorSidebar/Collection/gql.ts +++ b/src/views/ArticleDetail/AuthorSidebar/Collection/gql.ts @@ -1,5 +1,7 @@ import gql from 'graphql-tag' +import { ArticleDigestSidebar } from '~/components' + export const AUTHOR_SIDEBAR_COLLECTION = gql` query AuthorSidebarCollection($id: ID!, $after: String) { node(input: { id: $id }) { @@ -17,13 +19,12 @@ export const AUTHOR_SIDEBAR_COLLECTION = gql` edges { cursor node { - id - title - cover + ...ArticleDigestSidebarArticle } } } } } } + ${ArticleDigestSidebar.fragments.article} ` diff --git a/src/views/ArticleDetail/AuthorSidebar/Collection/index.tsx b/src/views/ArticleDetail/AuthorSidebar/Collection/index.tsx index 6d97506add..2e47fc539d 100644 --- a/src/views/ArticleDetail/AuthorSidebar/Collection/index.tsx +++ b/src/views/ArticleDetail/AuthorSidebar/Collection/index.tsx @@ -1,11 +1,22 @@ -import { toPath } from '~/common/utils' -import { LinkWrapper, QueryError, Throw404, usePublicQuery } from '~/components' +import { analytics, mergeConnections, toPath } from '~/common/utils' +import { + ArticleDigestAuthorSidebar, + InfiniteScroll, + LinkWrapper, + List, + QueryError, + Throw404, + usePublicQuery, +} from '~/components' import { ArticleDetailPublicQuery, AuthorSidebarCollectionQuery, } from '~/gql/graphql' -import { FeedPlaceholder } from '../Placeholder' +import { + ArticleDigestAuthorSidebarFeedPlaceholder, + FeedPlaceholder, +} from '../Placeholder' import { AUTHOR_SIDEBAR_COLLECTION } from './gql' import styles from './styles.module.css' @@ -18,12 +29,10 @@ export const Collection = ({ article, collectionId }: CollectionProps) => { /** * Data Fetching */ - const { data, loading, error } = usePublicQuery( - AUTHOR_SIDEBAR_COLLECTION, - { + const { data, loading, error, fetchMore } = + usePublicQuery(AUTHOR_SIDEBAR_COLLECTION, { variables: { id: collectionId }, - } - ) + }) const collection = data?.node! /** @@ -41,6 +50,28 @@ export const Collection = ({ article, collectionId }: CollectionProps) => { return } + // pagination + const connectionPath = 'node.articles' + const { edges, pageInfo } = collection?.articles || {} + + // load next page + const loadMore = async () => { + analytics.trackEvent('load_more', { + type: 'article_detail_author-sidebar-collection', + location: edges?.length || 0, + }) + + await fetchMore({ + variables: { after: pageInfo?.endCursor }, + updateQuery: (previousResult, { fetchMoreResult }) => + mergeConnections({ + oldData: previousResult, + newData: fetchMoreResult, + path: connectionPath, + }), + }) + } + const collectionDetailPath = toPath({ page: 'collectionDetail', userName: article?.author.userName || '', @@ -61,6 +92,26 @@ export const Collection = ({ article, collectionId }: CollectionProps) => { )} +
+ } + > + + {edges?.map(({ node, cursor }, i) => ( + + + + ))} + + +
) } diff --git a/src/views/ArticleDetail/AuthorSidebar/Collection/styles.module.css b/src/views/ArticleDetail/AuthorSidebar/Collection/styles.module.css index 3b224cea69..a31710af67 100644 --- a/src/views/ArticleDetail/AuthorSidebar/Collection/styles.module.css +++ b/src/views/ArticleDetail/AuthorSidebar/Collection/styles.module.css @@ -23,4 +23,14 @@ font-size: var(--font-size-xs); color: var(--color-grey-darker); } + + & .feed { + @mixin border-top-grey; + + max-height: 18.5rem; + padding-top: var(--spacing-tight); + padding-bottom: var(--spacing-x-loose); + overflow-y: auto; + border-top-style: dashed; + } } diff --git a/src/views/ArticleDetail/AuthorSidebar/Placeholder/index.tsx b/src/views/ArticleDetail/AuthorSidebar/Placeholder/index.tsx index cbf4bd1533..cfa5cf1840 100644 --- a/src/views/ArticleDetail/AuthorSidebar/Placeholder/index.tsx +++ b/src/views/ArticleDetail/AuthorSidebar/Placeholder/index.tsx @@ -1,3 +1,5 @@ +import { ArticleDigestAuthorSidebar } from '~/components' + import { Placeholder as AuthorPlaceholder } from '../Author/Placeholder' import styles from './styles.module.css' @@ -23,6 +25,16 @@ export const FeedPlaceholder = () => { ) } +export const ArticleDigestAuthorSidebarFeedPlaceholder = () => { + return ( +
+ + + +
+ ) +} + export const Placeholder = () => { return (