From 1a23c7dff76f49cb562e09671ffee184c5e4a7fd Mon Sep 17 00:00:00 2001 From: mydearxym Date: Sat, 9 Oct 2021 12:50:49 +0800 Subject: [PATCH 1/8] refactor(article-list): handle upvote action on post-list & re-org --- src/components/PostItem/DesktopView/index.tsx | 18 ++++-- src/components/PostItem/index.tsx | 7 +-- src/components/Upvote/BlogListLayout.tsx | 2 +- src/components/Upvote/PostListLayout.tsx | 2 +- src/components/Upvote/WorksArticleLayout.tsx | 6 +- src/components/Upvote/WorksCardLayout.tsx | 2 +- src/containers/thread/ArticlesThread/logic.ts | 60 ++++++++++++------- .../thread/ArticlesThread/schema.ts | 3 +- src/containers/thread/ArticlesThread/store.ts | 39 ++++++++++-- src/containers/tool/ArticleSticker/logic.ts | 12 +--- src/containers/tool/ArticleSticker/schema.ts | 27 +-------- src/schemas/fragments/base.ts | 23 +++++++ src/schemas/fragments/index.ts | 4 ++ src/schemas/pages/blog.ts | 1 + src/schemas/pages/job.ts | 1 + src/schemas/pages/meetup.ts | 1 + src/schemas/pages/post.ts | 1 + src/schemas/pages/radar.ts | 1 + src/schemas/pages/repo.ts | 1 + src/schemas/pages/works.ts | 1 + utils/async/sr71.js | 6 +- utils/constant/event.ts | 1 + utils/helper.ts | 10 ++++ utils/macros.ts | 49 +++++++++++++++ 24 files changed, 200 insertions(+), 78 deletions(-) create mode 100755 utils/macros.ts diff --git a/src/components/PostItem/DesktopView/index.tsx b/src/components/PostItem/DesktopView/index.tsx index d400d2dc8..62256d02c 100644 --- a/src/components/PostItem/DesktopView/index.tsx +++ b/src/components/PostItem/DesktopView/index.tsx @@ -1,10 +1,12 @@ import { FC, Fragment, memo } from 'react' -import type { TPost, TUser, TAccount } from '@/spec' +import type { TPost } from '@/spec' import { UPVOTE_LAYOUT } from '@/constant' +import { upvoteOnArticleList } from '@/utils/helper' import TheAvatar from '@/components/TheAvatar' import Upvote from '@/components/Upvote' + import { ArticleReadLabel, ArticlePinLabel } from '@/components/dynamic' import Header from './Header' @@ -13,11 +15,10 @@ import Body from './Body' import { AvatarWrapper, UpvoteWrapper, Main } from '../styles/desktop_view' type TProps = { - active?: TPost | null entry: TPost - onUserSelect?: (obj: TUser) => void - onAuthorSelect?: (obj: TAccount) => void + // onUserSelect?: (obj: TUser) => void + // onAuthorSelect?: (obj: TAccount) => void } const DigestView: FC = ({ entry }) => { @@ -28,7 +29,14 @@ const DigestView: FC = ({ entry }) => { - + + upvoteOnArticleList(entry, viewerHasUpvoted) + } + />
diff --git a/src/components/PostItem/index.tsx b/src/components/PostItem/index.tsx index 17acf0ae9..c758177ee 100755 --- a/src/components/PostItem/index.tsx +++ b/src/components/PostItem/index.tsx @@ -5,7 +5,6 @@ */ import { FC, memo } from 'react' -import dynamic from 'next/dynamic' import type { TPost, TUser, TAccount, TC11N } from '@/spec' import { buildLog } from '@/utils/logger' @@ -39,11 +38,7 @@ const PostItem: FC = ({ return ( {!isMobile ? ( - + ) : ( )} diff --git a/src/components/Upvote/BlogListLayout.tsx b/src/components/Upvote/BlogListLayout.tsx index b80d18d00..80fa161e7 100644 --- a/src/components/Upvote/BlogListLayout.tsx +++ b/src/components/Upvote/BlogListLayout.tsx @@ -35,7 +35,7 @@ const Upvote: FC = ({ - + ) diff --git a/src/components/Upvote/PostListLayout.tsx b/src/components/Upvote/PostListLayout.tsx index 2348a91e8..5453adae7 100644 --- a/src/components/Upvote/PostListLayout.tsx +++ b/src/components/Upvote/PostListLayout.tsx @@ -35,7 +35,7 @@ const Upvote: FC = ({ - + ) diff --git a/src/components/Upvote/WorksArticleLayout.tsx b/src/components/Upvote/WorksArticleLayout.tsx index 34fcbd4ec..3c3fc1a33 100644 --- a/src/components/Upvote/WorksArticleLayout.tsx +++ b/src/components/Upvote/WorksArticleLayout.tsx @@ -57,7 +57,11 @@ const Upvote: FC = ({ onAction={onAction} /> - + diff --git a/src/components/Upvote/WorksCardLayout.tsx b/src/components/Upvote/WorksCardLayout.tsx index 403f4f599..1979585d1 100644 --- a/src/components/Upvote/WorksCardLayout.tsx +++ b/src/components/Upvote/WorksCardLayout.tsx @@ -35,7 +35,7 @@ const Upvote: FC = ({ - + ) diff --git a/src/containers/thread/ArticlesThread/logic.ts b/src/containers/thread/ArticlesThread/logic.ts index 060809069..aabb564b8 100755 --- a/src/containers/thread/ArticlesThread/logic.ts +++ b/src/containers/thread/ArticlesThread/logic.ts @@ -1,12 +1,13 @@ import { useEffect } from 'react' -import type { TArticle, TArticleFilter } from '@/spec' -import { TYPE, EVENT, ERR } from '@/constant' +import type { TArticle, TThread, TArticleFilter } from '@/spec' +import { TYPE, EVENT, ERR, THREAD } from '@/constant' import { scrollToHeader } from '@/utils/dom' import asyncSuit from '@/utils/async' import { buildLog } from '@/utils/logger' -import { errRescue, titleCase, previewArticle } from '@/utils/helper' +import { errRescue, titleCase, previewArticle, authWarn } from '@/utils/helper' +import { matchPagedArticles, matchArticleUpvotes } from '@/utils/macros' import type { TStore } from './store' import S from './schema' @@ -23,6 +24,7 @@ const sr71$ = new SR71({ EVENT.ARTICLE_THREAD_CHANGE, EVENT.COMMUNITY_CHANGE, EVENT.C11N_DENSITY_CHANGE, + EVENT.UPVOTE_ON_ARTICLE_LIST, ], }) @@ -67,26 +69,37 @@ const onPreview = (article: TArticle): void => { previewArticle(article) } +const handleArticleUpvote = ( + article: TArticle, + viewerHasUpvoted: boolean, +): void => { + if (!store.isLogin) return authWarn({ hideToast: true }) + const { id, meta } = article + + store.updateUpvote(id, viewerHasUpvoted) + + viewerHasUpvoted + ? sr71$.mutate(S.getUpvoteSchema(meta.thread), { id }) + : sr71$.mutate(S.getUndoUpvoteSchema(meta.thread), { id }) +} + +const handleUovoteRes = ({ id, upvotesCount }) => + store.updateUpvoteCount(id, upvotesCount) + +const handlePagedArticlesRes = (thread: TThread, pagedArticles): void => { + const key = `paged${titleCase(thread)}s` + store.markRes({ [key]: pagedArticles }) +} + // ############################### // Data & Error handlers // ############################### const DataSolver = [ - { - match: asyncRes('pagedPosts'), - action: ({ pagedPosts }) => store.markRes({ pagedPosts }), - }, - { - match: asyncRes('pagedJobs'), - action: ({ pagedJobs }) => store.markRes({ pagedJobs }), - }, - { - match: asyncRes('pagedBlogs'), - action: ({ pagedBlogs }) => store.markRes({ pagedBlogs }), - }, - { - match: asyncRes('pagedRadars'), - action: ({ pagedRadars }) => store.markRes({ pagedRadars }), - }, + ...matchPagedArticles( + [THREAD.POST, THREAD.BLOG, THREAD.JOB, THREAD.RADAR], + handlePagedArticlesRes, + ), + ...matchArticleUpvotes(handleUovoteRes), { match: asyncRes(EVENT.COMMUNITY_CHANGE), action: () => loadArticles(), @@ -102,11 +115,16 @@ const DataSolver = [ onPreview(article) }, }, + { + match: asyncRes(EVENT.UPVOTE_ON_ARTICLE_LIST), + action: (res) => { + const { article, viewerHasUpvoted } = res[EVENT.UPVOTE_ON_ARTICLE_LIST] + handleArticleUpvote(article, viewerHasUpvoted) + }, + }, { match: asyncRes(EVENT.REFRESH_ARTICLES), action: (res) => { - console.log('EVENT.REFRESH_ARTICLES: ', res) - const { page = 1 } = res[EVENT.REFRESH_ARTICLES] loadArticles(page) }, diff --git a/src/containers/thread/ArticlesThread/schema.ts b/src/containers/thread/ArticlesThread/schema.ts index d21daaa22..596bb636c 100755 --- a/src/containers/thread/ArticlesThread/schema.ts +++ b/src/containers/thread/ArticlesThread/schema.ts @@ -16,7 +16,6 @@ const pagedRadars = gql` const pagedArticleTags = gql` ${P.pagedArticleTags} ` - const pagedCommunities = gql` query($filter: CommunitiesFilter!) { pagedCommunities(filter: $filter) { @@ -37,6 +36,8 @@ const schema = { pagedRadars, pagedArticleTags, pagedCommunities, + getUpvoteSchema: F.getUpvoteSchema, + getUndoUpvoteSchema: F.getUndoUpvoteSchema, } export default schema diff --git a/src/containers/thread/ArticlesThread/store.ts b/src/containers/thread/ArticlesThread/store.ts index b01646895..4ece5d2ba 100755 --- a/src/containers/thread/ArticlesThread/store.ts +++ b/src/containers/thread/ArticlesThread/store.ts @@ -7,6 +7,7 @@ import { merge, isEmpty, findIndex, propEq, pickBy, values } from 'ramda' import type { TRootStore, + TID, TTag, TAccount, TArticle, @@ -42,6 +43,10 @@ const ArticlesThread = T.model('ArticlesThread', { ), }) .views((self) => ({ + get isLogin(): boolean { + const root = getParent(self) as TRootStore + return root.account.isLogin + }, get curCommunity(): TCommunity { const root = getParent(self) as TRootStore return toJS(root.viewing.community) @@ -143,10 +148,36 @@ const ArticlesThread = T.model('ArticlesThread', { const { entries } = slf.pagedArticlesData const index = findIndex(propEq('id', id), entries as Record<'id', any>[]) - if (index >= 0) { - const pagedThreadKey = `paged${titleCase(slf.curThread)}s` - self[pagedThreadKey].entries[index].viewerHasViewed = true - } + if (index < 0) return + const pagedThreadKey = `paged${titleCase(slf.curThread)}s` + self[pagedThreadKey].entries[index].viewerHasViewed = true + }, + updateUpvote(id: TID, viewerHasUpvoted: boolean): void { + const slf = self as TStore + const { entries } = slf.pagedArticlesData + + const index = findIndex(propEq('id', id), entries as Record<'id', any>[]) + if (index < 0) return + + const pagedThreadKey = `paged${titleCase(slf.curThread)}s` + let curUpvotesCount = self[pagedThreadKey].entries[index].upvotesCount + + curUpvotesCount = viewerHasUpvoted + ? (curUpvotesCount += 1) + : (curUpvotesCount -= 1) + + self[pagedThreadKey].entries[index].upvotesCount = curUpvotesCount + self[pagedThreadKey].entries[index].viewerHasUpvoted = viewerHasUpvoted + }, + updateUpvoteCount(id: TID, count: number): void { + const slf = self as TStore + const { entries } = slf.pagedArticlesData + + const index = findIndex(propEq('id', id), entries as Record<'id', any>[]) + if (index < 0) return + + const pagedThreadKey = `paged${titleCase(slf.curThread)}s` + self[pagedThreadKey].entries[index].upvotesCount = count }, markRoute(params): void { const query = { ...self.tagQuery, ...self.filtersData, ...params } diff --git a/src/containers/tool/ArticleSticker/logic.ts b/src/containers/tool/ArticleSticker/logic.ts index b18574a13..a6b24aad1 100755 --- a/src/containers/tool/ArticleSticker/logic.ts +++ b/src/containers/tool/ArticleSticker/logic.ts @@ -5,6 +5,7 @@ import { EVENT } from '@/constant' import { send, authWarn } from '@/utils/helper' import { buildLog } from '@/utils/logger' import asyncSuit from '@/utils/async' +import { matchArticleUpvotes } from '@/utils/macros' import S from './schema' import type { TStore } from './store' @@ -55,25 +56,18 @@ export const loadPagedCommentsParticipants = (): void => { } // update the real upvoteCount after upvote action -const handleUovoteAfter = ({ upvotesCount }) => { +const handleUovoteRes = ({ upvotesCount }) => { store.updateUpvoteCount(upvotesCount) } const DataSolver = [ + ...matchArticleUpvotes(handleUovoteRes), { match: asyncRes('pagedCommentsParticipants'), action: ({ pagedCommentsParticipants }) => { store.mark({ pagedCommentsParticipants }) }, }, - { - match: asyncRes('upvotePost'), - action: ({ upvotePost }) => handleUovoteAfter(upvotePost), - }, - { - match: asyncRes('undoUpvotePost'), - action: ({ undoUpvotePost }) => handleUovoteAfter(undoUpvotePost), - }, ] const ErrSolver = [] diff --git a/src/containers/tool/ArticleSticker/schema.ts b/src/containers/tool/ArticleSticker/schema.ts index 6f9ba22c5..25debe416 100644 --- a/src/containers/tool/ArticleSticker/schema.ts +++ b/src/containers/tool/ArticleSticker/schema.ts @@ -1,6 +1,5 @@ import { gql } from '@urql/core' import { F } from '@/schemas' -import { titleCase } from '@/utils/helper' const pagedCommentsParticipants = gql` query($id: ID!, $thread: Thread, $filter: PagedFilter!) { @@ -14,32 +13,10 @@ const pagedCommentsParticipants = gql` } ` -const getUpvoteSchema = (thread) => { - return gql` - mutation ($id: ID!) { - upvote${titleCase(thread)}(id: $id) { - id - upvotesCount - } - } - ` -} - -const getUndoUpvoteSchema = (thread) => { - return gql` - mutation ($id: ID!) { - undoUpvote${titleCase(thread)}(id: $id) { - id - upvotesCount - } - } - ` -} - const schema = { pagedCommentsParticipants, - getUpvoteSchema, - getUndoUpvoteSchema, + getUpvoteSchema: F.getUpvoteSchema, + getUndoUpvoteSchema: F.getUndoUpvoteSchema, } export default schema diff --git a/src/schemas/fragments/base.ts b/src/schemas/fragments/base.ts index 89c9ebb42..a261f8136 100755 --- a/src/schemas/fragments/base.ts +++ b/src/schemas/fragments/base.ts @@ -1,3 +1,4 @@ +import { gql } from '@urql/core' import { values, flatten } from 'ramda' import { EMOTION } from '@/constant' @@ -248,3 +249,25 @@ export const pagedCounts = ` pageSize pageNumber ` + +export const getUpvoteSchema = (thread) => { + return gql` + mutation ($id: ID!) { + upvote${titleCase(thread)}(id: $id) { + id + upvotesCount + } + } + ` +} + +export const getUndoUpvoteSchema = (thread) => { + return gql` + mutation ($id: ID!) { + undoUpvote${titleCase(thread)}(id: $id) { + id + upvotesCount + } + } + ` +} diff --git a/src/schemas/fragments/index.ts b/src/schemas/fragments/index.ts index 47f29d7e9..5e842b500 100755 --- a/src/schemas/fragments/index.ts +++ b/src/schemas/fragments/index.ts @@ -25,6 +25,8 @@ import { emotionQuery, commentParent, pagedCounts, + getUpvoteSchema, + getUndoUpvoteSchema, } from './base' import { pagedPosts, pagedJobs, pagedRepos } from './paged' @@ -51,6 +53,8 @@ const F = { emotionQuery, commentParent, pagedCounts, + getUpvoteSchema, + getUndoUpvoteSchema, } export default F diff --git a/src/schemas/pages/blog.ts b/src/schemas/pages/blog.ts index e73cf70b9..459fb2ae0 100755 --- a/src/schemas/pages/blog.ts +++ b/src/schemas/pages/blog.ts @@ -22,6 +22,7 @@ export const pagedBlogs = ` ${F.author} } viewerHasViewed @include(if: $userHasLogin) + viewerHasUpvoted @include(if: $userHasLogin) } ${F.pagedCounts} } diff --git a/src/schemas/pages/job.ts b/src/schemas/pages/job.ts index edaef7db3..ee4dab042 100755 --- a/src/schemas/pages/job.ts +++ b/src/schemas/pages/job.ts @@ -19,6 +19,7 @@ export const pagedJobs = ` company companyLink viewerHasViewed @include(if: $userHasLogin) + viewerHasUpvoted @include(if: $userHasLogin) } ${F.pagedCounts} } diff --git a/src/schemas/pages/meetup.ts b/src/schemas/pages/meetup.ts index 2ef0aea8f..4ea3e7197 100755 --- a/src/schemas/pages/meetup.ts +++ b/src/schemas/pages/meetup.ts @@ -20,6 +20,7 @@ export const pagedMeetups = ` ${F.author} } viewerHasViewed @include(if: $userHasLogin) + viewerHasUpvoted @include(if: $userHasLogin) } ${F.pagedCounts} } diff --git a/src/schemas/pages/post.ts b/src/schemas/pages/post.ts index 830ca225b..f1c5d7538 100755 --- a/src/schemas/pages/post.ts +++ b/src/schemas/pages/post.ts @@ -22,6 +22,7 @@ export const pagedPosts = ` ${F.author} } viewerHasViewed @include(if: $userHasLogin) + viewerHasUpvoted @include(if: $userHasLogin) } ${F.pagedCounts} } diff --git a/src/schemas/pages/radar.ts b/src/schemas/pages/radar.ts index 04c74b850..bfbdbdc69 100755 --- a/src/schemas/pages/radar.ts +++ b/src/schemas/pages/radar.ts @@ -17,6 +17,7 @@ export const pagedRadars = ` digest linkAddr viewerHasViewed @include(if: $userHasLogin) + viewerHasUpvoted @include(if: $userHasLogin) } ${F.pagedCounts} } diff --git a/src/schemas/pages/repo.ts b/src/schemas/pages/repo.ts index a1da1f016..3d3d541c8 100755 --- a/src/schemas/pages/repo.ts +++ b/src/schemas/pages/repo.ts @@ -51,6 +51,7 @@ export const pagedRepos = ` ${F.tag} } viewerHasViewed @include(if: $userHasLogin) + viewerHasUpvoted @include(if: $userHasLogin) } ${F.pagedCounts} } diff --git a/src/schemas/pages/works.ts b/src/schemas/pages/works.ts index fbab03502..24c8f930d 100755 --- a/src/schemas/pages/works.ts +++ b/src/schemas/pages/works.ts @@ -20,6 +20,7 @@ export const pagedWorks = ` ${F.author} } viewerHasViewed @include(if: $userHasLogin) + viewerHasUpvoted @include(if: $userHasLogin) } ${F.pagedCounts} } diff --git a/utils/async/sr71.js b/utils/async/sr71.js index eb7d52535..340b0640f 100755 --- a/utils/async/sr71.js +++ b/utils/async/sr71.js @@ -81,9 +81,9 @@ class SR71 { // if (isEmptyValue(event)) return false // PubSub.unsubscribe(event) // avoid duplicate subscribe caused by HMR - PubSub.subscribe(event, (event, data) => - this.eventInput$.next({ [event]: data }), - ) + PubSub.subscribe(event, (event, data) => { + this.eventInput$.next({ [event]: data }) + }) } stop() { diff --git a/utils/constant/event.ts b/utils/constant/event.ts index aea4f7cd7..be24ed62c 100755 --- a/utils/constant/event.ts +++ b/utils/constant/event.ts @@ -16,6 +16,7 @@ const EVENT = { CONTENT_DRAGABLE: 'CONTENT_DRAGABLE', }, + UPVOTE_ON_ARTICLE_LIST: 'UPVOTE_ON_ARTICLE_LIST', // share SHARE: 'SHARE', diff --git a/utils/helper.ts b/utils/helper.ts index 9a1b64d61..86ac05e5f 100755 --- a/utils/helper.ts +++ b/utils/helper.ts @@ -192,6 +192,16 @@ export const setTag = (): void => { send(EVENT.SET_TAG, {}) } +export const upvoteOnArticleList = ( + article: TArticle, + viewerHasUpvoted: boolean, +): void => { + send(EVENT.UPVOTE_ON_ARTICLE_LIST, { + article, + viewerHasUpvoted, + }) +} + export const authWarn = (option): void => send(EVENT.AUTH_WARNING, option) /** diff --git a/utils/macros.ts b/utils/macros.ts new file mode 100755 index 000000000..b6ae8a7ea --- /dev/null +++ b/utils/macros.ts @@ -0,0 +1,49 @@ +// is not REAL macros, but some shared hanlder snippets +// NOTE: for consistency, function name shoud start with "match" + +import type { TThread } from '@/spec' +import { reject, map, includes, flatten } from 'ramda' + +import { ARTICLE_THREAD, THREAD } from '@/constant' +import { titleCase } from './helper' +import asyncSuite from './async' + +const { asyncRes } = asyncSuite + +// handle articles thread general pagedXXX response +export const matchPagedArticles = (threads: TThread[], callback) => { + return map((thread) => { + return { + match: asyncRes(`paged${titleCase(thread)}s`), + action: (res) => callback?.(thread, res[`paged${titleCase(thread)}s`]), + } + }, threads) +} + +/** + * match upvote/undo-upvotes article async response + * 处理 article 类型的 upvote/undo-upvote GraphQL 返回逻辑 + * Works 和 Repo 是处理页面,自行处理相关逻辑 + */ +export const matchArticleUpvotes = (callback) => { + const articleThreads = reject( + (t) => includes(t, [THREAD.WORKS, THREAD.REPO]), + ARTICLE_THREAD, + ) + + // @ts-ignore + const matches = map((thread) => { + return [ + { + match: asyncRes(`upvote${titleCase(thread)}`), + action: (res) => callback?.(res[`upvote${titleCase(thread)}`]), + }, + { + match: asyncRes(`undoUpvote${titleCase(thread)}`), + action: (res) => callback?.(res[`undoUpvote${titleCase(thread)}`]), + }, + ] + }, articleThreads) + + return flatten(matches) +} From 9508997de1cf5eb5a6c22f537e3e3f8e420b7b73 Mon Sep 17 00:00:00 2001 From: mydearxym Date: Sat, 9 Oct 2021 16:56:18 +0800 Subject: [PATCH 2/8] refactor(article-viewer): re-org with real data --- src/components/ArticleReadLabel/index.tsx | 2 +- src/components/AvatarsRow/RealAvatar.tsx | 2 +- .../AvatarsRow/styles/real_avatar.ts | 2 +- .../SelectedEmotions/EmotionUnit.tsx | 10 +---- .../SelectedEmotions/UsersPanel.tsx | 8 ++-- .../SelectedEmotions/index.tsx | 2 +- src/components/EmotionSelector/index.tsx | 2 +- src/components/Tooltip/RealTooltip.tsx | 4 +- src/components/Tooltip/index.tsx | 1 + src/components/Upvote/DefaultLayout.tsx | 1 + src/components/Upvote/Desc.tsx | 7 +++- .../Upvote/styles/default_layout.ts | 1 - src/containers/thread/ArticlesThread/logic.ts | 12 ++---- .../thread/ArticlesThread/schema.ts | 25 ++++------- .../ArticleViewer/PostViewer/ArticleInfo.tsx | 12 +++++- .../ArticleViewer/PostViewer/FixedHeader.tsx | 10 ++++- src/containers/viewer/ArticleViewer/logic.ts | 28 +++++++++++-- src/containers/viewer/ArticleViewer/schema.ts | 41 ++++--------------- src/containers/viewer/ArticleViewer/store.ts | 8 ++++ src/schemas/pages/index.ts | 2 + utils/macros.ts | 11 ++--- 21 files changed, 97 insertions(+), 94 deletions(-) diff --git a/src/components/ArticleReadLabel/index.tsx b/src/components/ArticleReadLabel/index.tsx index bea662d5d..93297aaf2 100755 --- a/src/components/ArticleReadLabel/index.tsx +++ b/src/components/ArticleReadLabel/index.tsx @@ -29,7 +29,7 @@ const ArticleReadLabel: FC = ({ entry, top = 24, left = -30 }) => { if (entry.pin) return null // return if (!isLogin) return null - if (markViewed && viewerHasViewed) { + if (markViewed && !viewerHasViewed) { return } diff --git a/src/components/AvatarsRow/RealAvatar.tsx b/src/components/AvatarsRow/RealAvatar.tsx index fca1af984..58fba7879 100644 --- a/src/components/AvatarsRow/RealAvatar.tsx +++ b/src/components/AvatarsRow/RealAvatar.tsx @@ -18,7 +18,6 @@ import { type TProps = { user?: TUser size?: TAvatarSize - scrollPosition?: any popCardPlacement?: 'top' | 'bottom' onUserSelect: (user: TUser) => void } @@ -36,6 +35,7 @@ const RealAvatar: FC = ({ delay={0} contentHeight={getAvatarSize(size, 'number') as string} placement={popCardPlacement} + interactive={false} > ` padding: 0px 0px 0px 0px; position: relative; width: ${({ size }) => getLiSize(size)}; - z-index: 2; + /* z-index: 2; */ &:hover { z-index: 3; diff --git a/src/components/EmotionSelector/SelectedEmotions/EmotionUnit.tsx b/src/components/EmotionSelector/SelectedEmotions/EmotionUnit.tsx index d1ada86e5..0e6302fe2 100644 --- a/src/components/EmotionSelector/SelectedEmotions/EmotionUnit.tsx +++ b/src/components/EmotionSelector/SelectedEmotions/EmotionUnit.tsx @@ -33,14 +33,8 @@ const EmotionUnit: FC = ({ item, onAction }) => { return ( - } + content={} + interactive={false} noPadding > = ({ name, count, users, hasEmotioned }) => { +const UsersPanel: FC = ({ name, count, users }) => { const showUnit = users.length > count return ( @@ -34,7 +34,7 @@ const UsersPanel: FC = ({ name, count, users, hasEmotioned }) => { {users.slice(0, 5).map((u, index) => ( - {u.nickname} + {cutRest(u.nickname, 12)} {users.length - 1 !== index ? ',' : ''} ))} diff --git a/src/components/EmotionSelector/SelectedEmotions/index.tsx b/src/components/EmotionSelector/SelectedEmotions/index.tsx index 9c7b9463e..e668146f4 100644 --- a/src/components/EmotionSelector/SelectedEmotions/index.tsx +++ b/src/components/EmotionSelector/SelectedEmotions/index.tsx @@ -29,4 +29,4 @@ const SelectedEmotions: FC = ({ emotions, onAction }) => { ) } -export default memo(SelectedEmotions) +export default SelectedEmotions diff --git a/src/components/EmotionSelector/index.tsx b/src/components/EmotionSelector/index.tsx index 308d6b409..52bacf5f6 100755 --- a/src/components/EmotionSelector/index.tsx +++ b/src/components/EmotionSelector/index.tsx @@ -10,7 +10,7 @@ import IconButton from '@/components/Buttons/IconButton' import Tooltip from '@/components/Tooltip' import { emotionsCoverter } from './helper' -import SelectedEmotions from './SelectedEmotions' +import SelectedEmotions from './SelectedEmotions/index' import Panel from './Panel' import { Wrapper } from './styles' diff --git a/src/components/Tooltip/RealTooltip.tsx b/src/components/Tooltip/RealTooltip.tsx index 624f842fc..73bae5d7e 100755 --- a/src/components/Tooltip/RealTooltip.tsx +++ b/src/components/Tooltip/RealTooltip.tsx @@ -58,6 +58,7 @@ type TProps = { * 暂时没有精力看 Tippy 的具体实现,小心使用。 */ forceZIndex?: boolean + interactive?: boolean onShow?: () => void onHide?: () => void @@ -67,6 +68,7 @@ type TProps = { const Tooltip: FC = ({ children, noPadding = false, + interactive = true, onHide, onShow, placement = 'top', @@ -151,7 +153,7 @@ const Tooltip: FC = ({ duration, trigger, // see https://github.com/atomiks/tippyjs/issues/751#issuecomment-611979594 for detail - interactive: true, + interactive, onHide: () => { setInstance(null) diff --git a/src/components/Tooltip/index.tsx b/src/components/Tooltip/index.tsx index 1aec28f91..9b7bcc9ce 100755 --- a/src/components/Tooltip/index.tsx +++ b/src/components/Tooltip/index.tsx @@ -54,6 +54,7 @@ type TProps = { * 暂时没有精力看 Tippy 的具体实现,小心使用。 */ forceZIndex?: boolean + interactive?: boolean onShow?: () => void onHide?: () => void diff --git a/src/components/Upvote/DefaultLayout.tsx b/src/components/Upvote/DefaultLayout.tsx index 27718548f..6fbf33420 100644 --- a/src/components/Upvote/DefaultLayout.tsx +++ b/src/components/Upvote/DefaultLayout.tsx @@ -51,6 +51,7 @@ const Upvote: FC = ({ count={count} avatarsRowLimit={avatarsRowLimit} alias={alias} + viewerHasUpvoted={viewerHasUpvoted} /> ) diff --git a/src/components/Upvote/Desc.tsx b/src/components/Upvote/Desc.tsx index d9054d755..291171642 100644 --- a/src/components/Upvote/Desc.tsx +++ b/src/components/Upvote/Desc.tsx @@ -18,6 +18,7 @@ const log = buildLog('c:Upvote:Desc') type TProps = { count?: number showCount?: boolean + viewerHasUpvoted?: boolean avatarsRowLimit?: number noOne: boolean alias?: string // 觉得很赞(default), 觉得很酷(works), 学到了(blog), 感兴趣(meetup), 有意思(Radar) @@ -27,6 +28,7 @@ const Desc: FC = ({ noOne, count = 4, showCount = true, + viewerHasUpvoted = false, avatarsRowLimit = 3, alias = '觉得很赞', }) => { @@ -39,8 +41,9 @@ const Desc: FC = ({ - - + + + diff --git a/src/components/Upvote/styles/default_layout.ts b/src/components/Upvote/styles/default_layout.ts index 440c2740f..ab21e7ee1 100755 --- a/src/components/Upvote/styles/default_layout.ts +++ b/src/components/Upvote/styles/default_layout.ts @@ -20,5 +20,4 @@ export const Text = styled.div` color: ${theme('thread.articleDigest')}; font-size: 14px; margin-top: 1px; - margin-left: 3px; ` diff --git a/src/containers/thread/ArticlesThread/logic.ts b/src/containers/thread/ArticlesThread/logic.ts index aabb564b8..719eb9167 100755 --- a/src/containers/thread/ArticlesThread/logic.ts +++ b/src/containers/thread/ArticlesThread/logic.ts @@ -52,10 +52,9 @@ const loadArticles = (page = 1): void => { // do query paged articles const doQuery = (page: number): void => { - const endpoint = S[`paged${titleCase(store.curThread)}s`] const args = store.getLoadArgs(page) - console.log('args: ', args) - sr71$.query(endpoint, args) + log('args: ', args) + sr71$.query(S.getPagedArticlesSchema(store.curThread), args) } /** @@ -69,10 +68,7 @@ const onPreview = (article: TArticle): void => { previewArticle(article) } -const handleArticleUpvote = ( - article: TArticle, - viewerHasUpvoted: boolean, -): void => { +const handleUpvote = (article: TArticle, viewerHasUpvoted: boolean): void => { if (!store.isLogin) return authWarn({ hideToast: true }) const { id, meta } = article @@ -119,7 +115,7 @@ const DataSolver = [ match: asyncRes(EVENT.UPVOTE_ON_ARTICLE_LIST), action: (res) => { const { article, viewerHasUpvoted } = res[EVENT.UPVOTE_ON_ARTICLE_LIST] - handleArticleUpvote(article, viewerHasUpvoted) + handleUpvote(article, viewerHasUpvoted) }, }, { diff --git a/src/containers/thread/ArticlesThread/schema.ts b/src/containers/thread/ArticlesThread/schema.ts index 596bb636c..f7551af65 100755 --- a/src/containers/thread/ArticlesThread/schema.ts +++ b/src/containers/thread/ArticlesThread/schema.ts @@ -1,18 +1,14 @@ import { gql } from '@urql/core' import { P, F } from '@/schemas' -const pagedPosts = gql` - ${P.pagedPosts} -` -const pagedJobs = gql` - ${P.pagedJobs} -` -const pagedBlogs = gql` - ${P.pagedBlogs} -` -const pagedRadars = gql` - ${P.pagedRadars} -` +import { titleCase } from '@/utils/helper' + +const getPagedArticlesSchema = (thread) => { + return gql` + ${P[`paged${titleCase(thread)}s`]} + ` +} + const pagedArticleTags = gql` ${P.pagedArticleTags} ` @@ -30,12 +26,9 @@ const pagedCommunities = gql` ` const schema = { - pagedPosts, - pagedJobs, - pagedBlogs, - pagedRadars, pagedArticleTags, pagedCommunities, + getPagedArticlesSchema, getUpvoteSchema: F.getUpvoteSchema, getUndoUpvoteSchema: F.getUndoUpvoteSchema, } diff --git a/src/containers/viewer/ArticleViewer/PostViewer/ArticleInfo.tsx b/src/containers/viewer/ArticleViewer/PostViewer/ArticleInfo.tsx index 0b8576239..498201373 100644 --- a/src/containers/viewer/ArticleViewer/PostViewer/ArticleInfo.tsx +++ b/src/containers/viewer/ArticleViewer/PostViewer/ArticleInfo.tsx @@ -15,13 +15,14 @@ import { BaseWrapper, UpvoteWrapper, } from '../styles/post_viewer/article_info' +import { handleUpvote } from '../logic' type TProps = { article: TArticle } const ArticleInfo: FC = ({ article }) => { - const { upvotesCount, meta } = article + const { upvotesCount, viewerHasUpvoted, meta } = article return ( @@ -34,7 +35,14 @@ const ArticleInfo: FC = ({ article }) => { - + + handleUpvote(article, viewerHasUpvoted) + } + /> ) diff --git a/src/containers/viewer/ArticleViewer/PostViewer/FixedHeader.tsx b/src/containers/viewer/ArticleViewer/PostViewer/FixedHeader.tsx index 777f884fc..b631156b1 100644 --- a/src/containers/viewer/ArticleViewer/PostViewer/FixedHeader.tsx +++ b/src/containers/viewer/ArticleViewer/PostViewer/FixedHeader.tsx @@ -3,6 +3,7 @@ import type { TArticle } from '@/spec' import Upvote from '@/components/Upvote' import { Wrapper, ArticleTitle } from '../styles/post_viewer/fixed_header' +import { handleUpvote } from '../logic' type TProps = { article: TArticle @@ -10,12 +11,17 @@ type TProps = { } const FixedHeader: FC = ({ article, visible }) => { - const { upvotesCount, meta } = article + const { upvotesCount, viewerHasUpvoted, meta } = article return ( {article.title} - + handleUpvote(article, viewerHasUpvoted)} + /> ) } diff --git a/src/containers/viewer/ArticleViewer/logic.ts b/src/containers/viewer/ArticleViewer/logic.ts index b729e96c4..b9fe17683 100755 --- a/src/containers/viewer/ArticleViewer/logic.ts +++ b/src/containers/viewer/ArticleViewer/logic.ts @@ -5,8 +5,9 @@ import type { TArticle } from '@/spec' import { EVENT, ERR } from '@/constant' import { buildLog } from '@/utils/logger' -import { errRescue } from '@/utils/helper' +import { errRescue, authWarn } from '@/utils/helper' import asyncSuit from '@/utils/async' +import { matchArticleUpvotes } from '@/utils/macros' import S from './schema' import type { TStore } from './store' @@ -23,7 +24,19 @@ let sub$ = null /* eslint-disable-next-line */ const log = buildLog('L:ArticleViewer') -export const holder = 1 +export const handleUpvote = ( + article: TArticle, + viewerHasUpvoted: boolean, +): void => { + if (!store.isLogin) return authWarn({ hideToast: true }) + const { id, meta } = article + + store.updateUpvote(viewerHasUpvoted) + + viewerHasUpvoted + ? sr71$.mutate(S.getUpvoteSchema(meta.thread), { id }) + : sr71$.mutate(S.getUndoUpvoteSchema(meta.thread), { id }) +} const loadArticle = (): void => { const userHasLogin = store.isLogin @@ -31,8 +44,7 @@ const loadArticle = (): void => { const variables = { id, userHasLogin } markLoading() - const schema = S[meta.thread.toLowerCase()] - return sr71$.query(schema, variables) + return sr71$.query(S.getArticleSchema(meta.thread), variables) } const markLoading = (maybe = true) => store.mark({ loading: maybe }) @@ -49,8 +61,16 @@ const handleArticleLoaded = (article: TArticle): void => { // ############################### // init & uninit handlers // ############################### +const handleUovoteRes = ({ upvotesCount }) => { + store.updateUpvoteCount(upvotesCount) + + const { id, meta } = store.viewingArticle + const variables = { id, userHasLogin: true } + sr71$.query(S.getArticleSchema(meta.thread), variables) +} const DataSolver = [ + ...matchArticleUpvotes(handleUovoteRes), { match: asyncRes('post'), action: ({ post }) => handleArticleLoaded(post), diff --git a/src/containers/viewer/ArticleViewer/schema.ts b/src/containers/viewer/ArticleViewer/schema.ts index 08b6e019b..b40e47451 100755 --- a/src/containers/viewer/ArticleViewer/schema.ts +++ b/src/containers/viewer/ArticleViewer/schema.ts @@ -1,21 +1,11 @@ import { gql } from '@urql/core' import { F, P } from '@/schemas' -const post = gql` - ${P.post} -` -const job = gql` - ${P.job} -` -const blog = gql` - ${P.blog} -` -const radar = gql` - ${P.radar} -` -const works = gql` - ${P.works} -` +const getArticleSchema = (thread) => { + return gql` + ${P[thread.toLowerCase()]} + ` +} const setTag = gql` ${P.setTag} @@ -24,27 +14,12 @@ const unsetTag = gql` ${P.unsetTag} ` -const postComment = gql` - query post($id: ID!) { - post(id: $id) { - id - commentsParticipants { - ${F.author} - } - commentsCount - } - } -` - const schema = { - post, - job, - blog, - radar, - works, setTag, unsetTag, - postComment, + getArticleSchema, + getUpvoteSchema: F.getUpvoteSchema, + getUndoUpvoteSchema: F.getUndoUpvoteSchema, } export default schema diff --git a/src/containers/viewer/ArticleViewer/store.ts b/src/containers/viewer/ArticleViewer/store.ts index 0e400276a..f69bfb353 100755 --- a/src/containers/viewer/ArticleViewer/store.ts +++ b/src/containers/viewer/ArticleViewer/store.ts @@ -54,6 +54,14 @@ const ArticleViewer = T.model('ArticleViewer', { const root = getParent(self) as TRootStore root.viewing.syncViewingItem(item) }, + updateUpvote(viewerHasUpvoted: boolean): void { + const root = getParent(self) as TRootStore + return root.viewing.updateUpvote(viewerHasUpvoted) + }, + updateUpvoteCount(count: number): void { + const root = getParent(self) as TRootStore + return root.viewing.updateUpvoteCount(count) + }, mark(sobj: Record): void { markStates(sobj, self) }, diff --git a/src/schemas/pages/index.ts b/src/schemas/pages/index.ts index da8b96e77..53c145f8c 100755 --- a/src/schemas/pages/index.ts +++ b/src/schemas/pages/index.ts @@ -1,3 +1,5 @@ +import { gql } from '@urql/core' + import { pagedPosts, post } from './post' import { pagedJobs, job } from './job' import { pagedRadars, radar } from './radar' diff --git a/utils/macros.ts b/utils/macros.ts index b6ae8a7ea..60e78a8f3 100755 --- a/utils/macros.ts +++ b/utils/macros.ts @@ -2,9 +2,9 @@ // NOTE: for consistency, function name shoud start with "match" import type { TThread } from '@/spec' -import { reject, map, includes, flatten } from 'ramda' +import { map, values, flatten } from 'ramda' -import { ARTICLE_THREAD, THREAD } from '@/constant' +import { ARTICLE_THREAD } from '@/constant' import { titleCase } from './helper' import asyncSuite from './async' @@ -26,11 +26,6 @@ export const matchPagedArticles = (threads: TThread[], callback) => { * Works 和 Repo 是处理页面,自行处理相关逻辑 */ export const matchArticleUpvotes = (callback) => { - const articleThreads = reject( - (t) => includes(t, [THREAD.WORKS, THREAD.REPO]), - ARTICLE_THREAD, - ) - // @ts-ignore const matches = map((thread) => { return [ @@ -43,7 +38,7 @@ export const matchArticleUpvotes = (callback) => { action: (res) => callback?.(res[`undoUpvote${titleCase(thread)}`]), }, ] - }, articleThreads) + }, values(ARTICLE_THREAD)) return flatten(matches) } From f75bd0ef25625f48cb2a43ead791e2a24696383d Mon Sep 17 00:00:00 2001 From: mydearxym Date: Sat, 9 Oct 2021 21:46:51 +0800 Subject: [PATCH 3/8] refactor(article-viewer): upvote, viewer workflow, re-org --- src/containers/content/WorksContent/logic.ts | 5 -- src/containers/content/WorksContent/store.ts | 7 -- src/containers/digest/ArticleDigest/logic.ts | 6 +- src/containers/digest/ArticleDigest/store.ts | 4 +- src/containers/thread/ArticlesThread/logic.ts | 3 +- .../thread/ArticlesThread/schema.ts | 30 ++++---- src/containers/thread/ArticlesThread/store.ts | 68 ++++++++++--------- src/containers/thread/ReposThread/logic.js | 1 - src/containers/thread/ReposThread/store.js | 7 -- .../unit/ArticleViewerHeader/logic.js | 6 +- .../unit/ArticleViewerHeader/store.js | 4 +- src/containers/viewer/ArticleViewer/logic.ts | 49 +++++-------- src/containers/viewer/ArticleViewer/store.ts | 4 +- src/stores/ViewingStore/index.ts | 2 +- utils/macros.ts | 11 +++ 15 files changed, 95 insertions(+), 112 deletions(-) diff --git a/src/containers/content/WorksContent/logic.ts b/src/containers/content/WorksContent/logic.ts index 59d5f2221..93daed266 100755 --- a/src/containers/content/WorksContent/logic.ts +++ b/src/containers/content/WorksContent/logic.ts @@ -27,11 +27,6 @@ export const toggleSidebar = (): void => { } export const onPreview = (works: TWorks): void => { - const { setViewedFlag } = store - // const { setViewedFlag, resState } = store - // if (resState === TYPE.RES_STATE.LOADING) return - setTimeout(() => setViewedFlag(works.id), 1500) - previewArticle(works) } diff --git a/src/containers/content/WorksContent/store.ts b/src/containers/content/WorksContent/store.ts index db254214d..41720eee7 100755 --- a/src/containers/content/WorksContent/store.ts +++ b/src/containers/content/WorksContent/store.ts @@ -24,13 +24,6 @@ const WorksContent = T.model('WorksContent', { }, })) .actions((self) => ({ - setViewedFlag(id): void { - const { entries } = self.pagedWorksData - const index = findIndex(propEq('id', id), entries as Record<'id', any>[]) - if (index >= 0) { - self.pagedWorks.entries[index].viewerHasViewed = true - } - }, mark(sobj: Record): void { markStates(sobj, self) }, diff --git a/src/containers/digest/ArticleDigest/logic.ts b/src/containers/digest/ArticleDigest/logic.ts index 0099e3d8e..3fede249e 100755 --- a/src/containers/digest/ArticleDigest/logic.ts +++ b/src/containers/digest/ArticleDigest/logic.ts @@ -83,7 +83,7 @@ const DataSolver = [ match: asyncRes('post'), action: ({ post }) => { store.setViewing({ post: merge(store.viewingArticle, post) }) - store.syncViewingItem(post) + store.syncArticle(post) markLoading(false) }, }, @@ -91,7 +91,7 @@ const DataSolver = [ match: asyncRes('job'), action: ({ job }) => { store.setViewing({ job: merge(store.viewingArticle, job) }) - store.syncViewingItem(job) + store.syncArticle(job) markLoading(false) }, }, @@ -99,7 +99,7 @@ const DataSolver = [ match: asyncRes('repo'), action: ({ repo }) => { store.setViewing({ repo: merge(store.viewingArticle, repo) }) - store.syncViewingItem(repo) + store.syncArticle(repo) markLoading(false) }, }, diff --git a/src/containers/digest/ArticleDigest/store.ts b/src/containers/digest/ArticleDigest/store.ts index 8f73bd362..8308246c3 100755 --- a/src/containers/digest/ArticleDigest/store.ts +++ b/src/containers/digest/ArticleDigest/store.ts @@ -56,9 +56,9 @@ const ArticleDigest = T.model('ArticleDigest', { const root = getParent(self) as TRootStore root.setViewing(sobj) }, - syncViewingItem(item): void { + syncArticle(item): void { const root = getParent(self) as TRootStore - root.viewing.syncViewingItem(item) + root.viewing.syncArticle(item) }, mark(sobj: Record): void { markStates(sobj, self) diff --git a/src/containers/thread/ArticlesThread/logic.ts b/src/containers/thread/ArticlesThread/logic.ts index 719eb9167..03a20bbc7 100755 --- a/src/containers/thread/ArticlesThread/logic.ts +++ b/src/containers/thread/ArticlesThread/logic.ts @@ -61,9 +61,8 @@ const doQuery = (page: number): void => { * prepack then send preview event to drawer */ const onPreview = (article: TArticle): void => { - const { setViewedFlag, resState } = store + const { resState } = store if (resState === TYPE.RES_STATE.LOADING) return - setTimeout(() => setViewedFlag(article.id), 1500) previewArticle(article) } diff --git a/src/containers/thread/ArticlesThread/schema.ts b/src/containers/thread/ArticlesThread/schema.ts index f7551af65..5c4a85048 100755 --- a/src/containers/thread/ArticlesThread/schema.ts +++ b/src/containers/thread/ArticlesThread/schema.ts @@ -9,26 +9,30 @@ const getPagedArticlesSchema = (thread) => { ` } -const pagedArticleTags = gql` - ${P.pagedArticleTags} -` -const pagedCommunities = gql` - query($filter: CommunitiesFilter!) { - pagedCommunities(filter: $filter) { - entries { - ${F.community} - contributesDigest - subscribersCount +const getArticleFreshSchema = (thread) => { + // TODO: commentParticipants + return gql` + query post($id: ID!, $userHasLogin: Boolean!) { + post(id: $id) { + id + views + upvotesCount + commentsCount + viewerHasViewed @include(if: $userHasLogin) + viewerHasUpvoted @include(if: $userHasLogin) } - ${F.pagedCounts} } - } + ` +} + +const pagedArticleTags = gql` + ${P.pagedArticleTags} ` const schema = { pagedArticleTags, - pagedCommunities, getPagedArticlesSchema, + getArticleFreshSchema, getUpvoteSchema: F.getUpvoteSchema, getUndoUpvoteSchema: F.getUndoUpvoteSchema, } diff --git a/src/containers/thread/ArticlesThread/store.ts b/src/containers/thread/ArticlesThread/store.ts index 4ece5d2ba..baee498a3 100755 --- a/src/containers/thread/ArticlesThread/store.ts +++ b/src/containers/thread/ArticlesThread/store.ts @@ -94,6 +94,10 @@ const ArticlesThread = T.model('ArticlesThread', { return !isEmpty(curFilter) || !isEmpty(pagedPosts.entries) }, + get pagedArticleKey(): string { + const slf = self as TStore + return `paged${titleCase(slf.curThread)}s` + }, })) .actions((self) => ({ // the args pass to server when load articles @@ -115,20 +119,22 @@ const ArticlesThread = T.model('ArticlesThread', { slf.mark(res) }, - - updateArticle(item: TArticle): void { + targetArticleIndex(id: TID): number | null { const slf = self as TStore const { entries } = slf.pagedArticlesData // @ts-ignore - const index = findIndex(propEq('id', item.id), entries) - if (index >= 0) { - // TODO: - // @ts-ignore - self.pagedPosts.entries[index] = merge( - toJS(self.pagedPosts.entries[index]), - item, - ) - } + const index = findIndex(propEq('id', id), entries) + if (index < 0) return null + + return index + }, + targetArticle(id: TID): TArticle | null { + const slf = self as TStore + const { pagedArticleKey } = slf + const index = slf.targetArticleIndex(id) + if (index < 0) return null + + return toJS(slf[pagedArticleKey].entries[index]) }, selectFilter(option: TArticleFilter): void { const curfilter = self.filtersData @@ -143,41 +149,37 @@ const ArticlesThread = T.model('ArticlesThread', { const root = getParent(self) as TRootStore root.setViewing(sobj) }, - setViewedFlag(id): void { + updateArticle(article: TArticle): void { const slf = self as TStore - const { entries } = slf.pagedArticlesData - const index = findIndex(propEq('id', id), entries as Record<'id', any>[]) + const { pagedArticleKey } = slf + const index = slf.targetArticleIndex(article.id) + if (index === null) return + const targetArticle = toJS(slf[pagedArticleKey].entries[index]) - if (index < 0) return - const pagedThreadKey = `paged${titleCase(slf.curThread)}s` - self[pagedThreadKey].entries[index].viewerHasViewed = true + slf[pagedArticleKey].entries[index] = merge(targetArticle, article) }, updateUpvote(id: TID, viewerHasUpvoted: boolean): void { const slf = self as TStore - const { entries } = slf.pagedArticlesData + const { pagedArticleKey } = slf - const index = findIndex(propEq('id', id), entries as Record<'id', any>[]) - if (index < 0) return + const index = slf.targetArticleIndex(id) + if (index === null) return + const targetArticle = toJS(slf[pagedArticleKey].entries[index]) - const pagedThreadKey = `paged${titleCase(slf.curThread)}s` - let curUpvotesCount = self[pagedThreadKey].entries[index].upvotesCount + let curCount = targetArticle.upvotesCount + curCount = viewerHasUpvoted ? (curCount += 1) : (curCount -= 1) - curUpvotesCount = viewerHasUpvoted - ? (curUpvotesCount += 1) - : (curUpvotesCount -= 1) - - self[pagedThreadKey].entries[index].upvotesCount = curUpvotesCount - self[pagedThreadKey].entries[index].viewerHasUpvoted = viewerHasUpvoted + self[pagedArticleKey].entries[index].upvotesCount = curCount + self[pagedArticleKey].entries[index].viewerHasUpvoted = viewerHasUpvoted }, updateUpvoteCount(id: TID, count: number): void { const slf = self as TStore - const { entries } = slf.pagedArticlesData + const { pagedArticleKey } = slf - const index = findIndex(propEq('id', id), entries as Record<'id', any>[]) - if (index < 0) return + const index = slf.targetArticleIndex(id) + if (index === null) return - const pagedThreadKey = `paged${titleCase(slf.curThread)}s` - self[pagedThreadKey].entries[index].upvotesCount = count + slf[pagedArticleKey].entries[index].upvotesCount = count }, markRoute(params): void { const query = { ...self.tagQuery, ...self.filtersData, ...params } diff --git a/src/containers/thread/ReposThread/logic.js b/src/containers/thread/ReposThread/logic.js index a15170a0d..a90a92d54 100755 --- a/src/containers/thread/ReposThread/logic.js +++ b/src/containers/thread/ReposThread/logic.js @@ -63,7 +63,6 @@ export const onPageChange = (page) => { } export const onPreview = (data) => { - setTimeout(() => store.setViewedFlag(data.id), 1500) send(EVENT.DRAWER.OPEN, { type: TYPE.DRAWER.REPO_VIEW, thread: THREAD.REPO, diff --git a/src/containers/thread/ReposThread/store.js b/src/containers/thread/ReposThread/store.js index b511c9ad9..d23bdd8a9 100755 --- a/src/containers/thread/ReposThread/store.js +++ b/src/containers/thread/ReposThread/store.js @@ -87,13 +87,6 @@ const ReposThread = T.model('ReposThread', { setViewing(sobj) { self.root.setViewing(sobj) }, - setViewedFlag(id) { - const { entries } = self.pagedReposData - const index = findIndex(propEq('id', id), entries) - if (index >= 0) { - self.pagedRepos.entries[index].viewerHasViewed = true - } - }, updateItem(item) { const { entries } = self.pagedReposData const index = findIndex(propEq('id', item.id), entries) diff --git a/src/containers/unit/ArticleViewerHeader/logic.js b/src/containers/unit/ArticleViewerHeader/logic.js index cf5e5a528..46647f669 100755 --- a/src/containers/unit/ArticleViewerHeader/logic.js +++ b/src/containers/unit/ArticleViewerHeader/logic.js @@ -76,7 +76,7 @@ const DataSolver = [ match: asyncRes('post'), action: ({ post }) => { store.setViewing({ post: merge(store.viewingArticle, post) }) - store.syncViewingItem(post) + store.syncArticle(post) markLoading(false) }, }, @@ -84,7 +84,7 @@ const DataSolver = [ match: asyncRes('job'), action: ({ job }) => { store.setViewing({ job: merge(store.viewingArticle, job) }) - store.syncViewingItem(job) + store.syncArticle(job) markLoading(false) }, }, @@ -92,7 +92,7 @@ const DataSolver = [ match: asyncRes('repo'), action: ({ repo }) => { store.setViewing({ repo: merge(store.viewingArticle, repo) }) - store.syncViewingItem(repo) + store.syncArticle(repo) markLoading(false) }, }, diff --git a/src/containers/unit/ArticleViewerHeader/store.js b/src/containers/unit/ArticleViewerHeader/store.js index c676344a4..cc92c9207 100755 --- a/src/containers/unit/ArticleViewerHeader/store.js +++ b/src/containers/unit/ArticleViewerHeader/store.js @@ -50,8 +50,8 @@ const ArticleViewerHeader = T.model('ArticleViewerHeader', { setViewing(sobj) { self.root.setViewing(sobj) }, - syncViewingItem(item) { - self.root.viewing.syncViewingItem(item) + syncArticle(item) { + self.root.viewing.syncArticle(item) }, mark(sobj) { markStates(sobj, self) diff --git a/src/containers/viewer/ArticleViewer/logic.ts b/src/containers/viewer/ArticleViewer/logic.ts index b9fe17683..cbb15e85c 100755 --- a/src/containers/viewer/ArticleViewer/logic.ts +++ b/src/containers/viewer/ArticleViewer/logic.ts @@ -7,7 +7,7 @@ import { EVENT, ERR } from '@/constant' import { buildLog } from '@/utils/logger' import { errRescue, authWarn } from '@/utils/helper' import asyncSuit from '@/utils/async' -import { matchArticleUpvotes } from '@/utils/macros' +import { matchArticleUpvotes, matchArticles } from '@/utils/macros' import S from './schema' import type { TStore } from './store' @@ -49,49 +49,36 @@ const loadArticle = (): void => { const markLoading = (maybe = true) => store.mark({ loading: maybe }) -const handleArticleLoaded = (article: TArticle): void => { - console.log('handleArticleLoaded: ', article) +const handleArticleRes = (article: TArticle): void => { + console.log('handleArticleRes: ', article) const thread = article.meta.thread.toLowerCase() store.setViewing({ [thread]: merge(store.viewingArticle, article) }) - store.syncViewingItem(article) markLoading(false) + + const { id, viewerHasUpvoted, views, upvotesCount } = article + store.syncArticle({ + id, + viewerHasUpvoted, + views, + upvotesCount, + viewerHasViewed: true, + }) } -// ############################### -// init & uninit handlers -// ############################### const handleUovoteRes = ({ upvotesCount }) => { store.updateUpvoteCount(upvotesCount) - const { id, meta } = store.viewingArticle - const variables = { id, userHasLogin: true } - sr71$.query(S.getArticleSchema(meta.thread), variables) + const { id, viewerHasUpvoted } = store.viewingArticle + store.syncArticle({ id, viewerHasUpvoted, upvotesCount }) } +// ############################### +// init & uninit handlers +// ############################### const DataSolver = [ ...matchArticleUpvotes(handleUovoteRes), - { - match: asyncRes('post'), - action: ({ post }) => handleArticleLoaded(post), - }, - { - match: asyncRes('job'), - action: ({ job }) => handleArticleLoaded(job), - }, - { - match: asyncRes('blog'), - action: ({ blog }) => handleArticleLoaded(blog), - }, - { - match: asyncRes('works'), - action: ({ works }) => { - console.log('got works:', works) - store.setViewing({ works: merge(store.viewingArticle, works) }) - // store.syncViewingItem(works) // do in works content - markLoading(false) - }, - }, + ...matchArticles(handleArticleRes), { match: asyncRes(EVENT.RELOAD_ARTICLE), action: () => { diff --git a/src/containers/viewer/ArticleViewer/store.ts b/src/containers/viewer/ArticleViewer/store.ts index f69bfb353..7ff2ca5fc 100755 --- a/src/containers/viewer/ArticleViewer/store.ts +++ b/src/containers/viewer/ArticleViewer/store.ts @@ -50,9 +50,9 @@ const ArticleViewer = T.model('ArticleViewer', { const root = getParent(self) as TRootStore root.setViewing(sobj) }, - syncViewingItem(item): void { + syncArticle(item): void { const root = getParent(self) as TRootStore - root.viewing.syncViewingItem(item) + root.articlesThread.updateArticle(item) }, updateUpvote(viewerHasUpvoted: boolean): void { const root = getParent(self) as TRootStore diff --git a/src/stores/ViewingStore/index.ts b/src/stores/ViewingStore/index.ts index 4faddb43f..e2110d69f 100755 --- a/src/stores/ViewingStore/index.ts +++ b/src/stores/ViewingStore/index.ts @@ -102,7 +102,7 @@ const ViewingStore = T.model('ViewingStore', { return mark({ user }) }, - syncViewingItem(item: TArticle): void { + syncArticle(item: TArticle): void { const root = getParent(self) as TRootStore root.articlesThread.updateArticle(item) }, diff --git a/utils/macros.ts b/utils/macros.ts index 60e78a8f3..03a3319f5 100755 --- a/utils/macros.ts +++ b/utils/macros.ts @@ -20,6 +20,17 @@ export const matchPagedArticles = (threads: TThread[], callback) => { }, threads) } +// +export const matchArticles = (callback) => { + // @ts-ignore + return map((thread) => { + return { + match: asyncRes(`${thread.toLowerCase()}`), + action: (res) => callback?.(res[`${thread.toLowerCase()}`]), + } + }, values(ARTICLE_THREAD)) +} + /** * match upvote/undo-upvotes article async response * 处理 article 类型的 upvote/undo-upvote GraphQL 返回逻辑 From 526d870ee92e9cf451b61f2c4a1e3466a0b93b2b Mon Sep 17 00:00:00 2001 From: mydearxym Date: Sat, 9 Oct 2021 23:27:32 +0800 Subject: [PATCH 4/8] refactor(utils): macros re-org & doc update --- utils/macros.ts | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/utils/macros.ts b/utils/macros.ts index 03a3319f5..14a327a8c 100755 --- a/utils/macros.ts +++ b/utils/macros.ts @@ -1,4 +1,4 @@ -// is not REAL macros, but some shared hanlder snippets +// those are not REAL macros, but some shared GraphQL response hanlder snippets // NOTE: for consistency, function name shoud start with "match" import type { TThread } from '@/spec' @@ -10,23 +10,31 @@ import asyncSuite from './async' const { asyncRes } = asyncSuite -// handle articles thread general pagedXXX response +/** + * handle general paged articles GQ response + */ export const matchPagedArticles = (threads: TThread[], callback) => { return map((thread) => { + const resKey = `paged${titleCase(thread)}s` + return { - match: asyncRes(`paged${titleCase(thread)}s`), - action: (res) => callback?.(thread, res[`paged${titleCase(thread)}s`]), + match: asyncRes(resKey), + action: (res) => callback?.(thread, res[resKey]), } }, threads) } -// +/** + * handle general article GQ response + */ export const matchArticles = (callback) => { // @ts-ignore return map((thread) => { + const resKey = `${thread.toLowerCase()}` + return { - match: asyncRes(`${thread.toLowerCase()}`), - action: (res) => callback?.(res[`${thread.toLowerCase()}`]), + match: asyncRes(resKey), + action: (res) => callback?.(res[resKey]), } }, values(ARTICLE_THREAD)) } @@ -34,19 +42,21 @@ export const matchArticles = (callback) => { /** * match upvote/undo-upvotes article async response * 处理 article 类型的 upvote/undo-upvote GraphQL 返回逻辑 - * Works 和 Repo 是处理页面,自行处理相关逻辑 */ export const matchArticleUpvotes = (callback) => { // @ts-ignore const matches = map((thread) => { + const upvoteResKey = `upvote${titleCase(thread)}` + const undoUpvoteResKey = `undoUpvote${titleCase(thread)}` + return [ { - match: asyncRes(`upvote${titleCase(thread)}`), - action: (res) => callback?.(res[`upvote${titleCase(thread)}`]), + match: asyncRes(upvoteResKey), + action: (res) => callback?.(res[upvoteResKey]), }, { - match: asyncRes(`undoUpvote${titleCase(thread)}`), - action: (res) => callback?.(res[`undoUpvote${titleCase(thread)}`]), + match: asyncRes(undoUpvoteResKey), + action: (res) => callback?.(res[undoUpvoteResKey]), }, ] }, values(ARTICLE_THREAD)) From 3508c2340a6200663541d0e424a6bf2122619f6e Mon Sep 17 00:00:00 2001 From: mydearxym Date: Sun, 10 Oct 2021 11:19:53 +0800 Subject: [PATCH 5/8] style(editor): editor delete action adjust --- package-lock.json | 32 +++++++++++-------- package.json | 2 +- .../editor/RichEditor/styles/overwrite.ts | 3 ++ 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/package-lock.json b/package-lock.json index ecee761be..e0bb177b2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "license": "Apache License 2.0", "dependencies": { "@babel/core": "^7.13.14", - "@groupher/react-editor": "^1.1.25", + "@groupher/react-editor": "^1.1.26", "@next/bundle-analyzer": "^9.4.4", "@sentry/browser": "5.17.0", "@sentry/node": "5.17.0", @@ -1496,8 +1496,9 @@ } }, "node_modules/@groupher/editor.js": { - "version": "2.19.37", - "integrity": "sha512-DjU+KC+MzL4DtNgN0SncztJk8q5v1nOZZPm+afpNoXgtbcaPlqtrLOTRHHSm5C7Hnf1fuBo0rmbDoYEcOqegIQ==", + "version": "2.19.38", + "resolved": "https://registry.npmjs.org/@groupher/editor.js/-/editor.js-2.19.38.tgz", + "integrity": "sha512-vxOkNZlqRKc7oRuWUFsgmzkN8KE8GwCG6s0vyEWM8ZX8wmUK0Fzvcs4lneodRCETESLOxnQzBj+cSHbaRF6Pew==", "dependencies": { "caniuse-lite": "^1.0.30001164", "codex-notifier": "^1.1.2", @@ -1533,9 +1534,9 @@ } }, "node_modules/@groupher/react-editor": { - "version": "1.1.25", - "resolved": "https://registry.npmjs.org/@groupher/react-editor/-/react-editor-1.1.25.tgz", - "integrity": "sha512-wf9uoKv+YH/AHJ6ALl4IVIfJaryfytFMsia1Y/glpOVFOfVCE5b6Pv+PVqyFmKPoDeYbEQePew5aOJL3twJ0HQ==", + "version": "1.1.26", + "resolved": "https://registry.npmjs.org/@groupher/react-editor/-/react-editor-1.1.26.tgz", + "integrity": "sha512-bDDKLMMVQHfu3igQqn8C3LGxobjtcAfO+1dhfkXapUGsEiutbLeZK+5hmz+BTpjQiWtLgkxU0tKCQtoG5Vmv+A==", "dependencies": { "@groupher/editor-alert": "^1.0.27", "@groupher/editor-code": "^1.0.30", @@ -1555,7 +1556,7 @@ "@groupher/editor-quote": "^2.3.9", "@groupher/editor-strike": "^0.1.3", "@groupher/editor-table": "^2.0.8", - "@groupher/editor.js": "^2.19.37", + "@groupher/editor.js": "^2.19.38", "editorjs-drag-drop": "^0.2.1", "editorjs-undo": "^0.3.0", "react": "^17.x", @@ -5549,10 +5550,12 @@ }, "node_modules/codex-notifier": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/codex-notifier/-/codex-notifier-1.1.2.tgz", "integrity": "sha512-DCp6xe/LGueJ1N5sXEwcBc3r3PyVkEEDNWCVigfvywAkeXcZMk9K41a31tkEFBW0Ptlwji6/JlAb49E3Yrxbtg==" }, "node_modules/codex-tooltip": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/codex-tooltip/-/codex-tooltip-1.0.2.tgz", "integrity": "sha512-oC+Bu5X/zyhbPydgMSLWKoM/+vkJMqaLWu3Dt/jZgXS3MWK23INwC5DMBrVXZSufAFk0i0SUni38k9rLMyZn/w==" }, "node_modules/collect-v8-coverage": { @@ -23520,8 +23523,9 @@ } }, "@groupher/editor.js": { - "version": "2.19.37", - "integrity": "sha512-DjU+KC+MzL4DtNgN0SncztJk8q5v1nOZZPm+afpNoXgtbcaPlqtrLOTRHHSm5C7Hnf1fuBo0rmbDoYEcOqegIQ==", + "version": "2.19.38", + "resolved": "https://registry.npmjs.org/@groupher/editor.js/-/editor.js-2.19.38.tgz", + "integrity": "sha512-vxOkNZlqRKc7oRuWUFsgmzkN8KE8GwCG6s0vyEWM8ZX8wmUK0Fzvcs4lneodRCETESLOxnQzBj+cSHbaRF6Pew==", "requires": { "caniuse-lite": "^1.0.30001164", "codex-notifier": "^1.1.2", @@ -23553,9 +23557,9 @@ } }, "@groupher/react-editor": { - "version": "1.1.25", - "resolved": "https://registry.npmjs.org/@groupher/react-editor/-/react-editor-1.1.25.tgz", - "integrity": "sha512-wf9uoKv+YH/AHJ6ALl4IVIfJaryfytFMsia1Y/glpOVFOfVCE5b6Pv+PVqyFmKPoDeYbEQePew5aOJL3twJ0HQ==", + "version": "1.1.26", + "resolved": "https://registry.npmjs.org/@groupher/react-editor/-/react-editor-1.1.26.tgz", + "integrity": "sha512-bDDKLMMVQHfu3igQqn8C3LGxobjtcAfO+1dhfkXapUGsEiutbLeZK+5hmz+BTpjQiWtLgkxU0tKCQtoG5Vmv+A==", "requires": { "@groupher/editor-alert": "^1.0.27", "@groupher/editor-code": "^1.0.30", @@ -23575,7 +23579,7 @@ "@groupher/editor-quote": "^2.3.9", "@groupher/editor-strike": "^0.1.3", "@groupher/editor-table": "^2.0.8", - "@groupher/editor.js": "^2.19.37", + "@groupher/editor.js": "^2.19.38", "editorjs-drag-drop": "^0.2.1", "editorjs-undo": "^0.3.0", "react": "^17.x", @@ -26777,10 +26781,12 @@ }, "codex-notifier": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/codex-notifier/-/codex-notifier-1.1.2.tgz", "integrity": "sha512-DCp6xe/LGueJ1N5sXEwcBc3r3PyVkEEDNWCVigfvywAkeXcZMk9K41a31tkEFBW0Ptlwji6/JlAb49E3Yrxbtg==" }, "codex-tooltip": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/codex-tooltip/-/codex-tooltip-1.0.2.tgz", "integrity": "sha512-oC+Bu5X/zyhbPydgMSLWKoM/+vkJMqaLWu3Dt/jZgXS3MWK23INwC5DMBrVXZSufAFk0i0SUni38k9rLMyZn/w==" }, "collect-v8-coverage": { diff --git a/package.json b/package.json index 73169ffb4..ab9522ac4 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ }, "dependencies": { "@babel/core": "^7.13.14", - "@groupher/react-editor": "^1.1.25", + "@groupher/react-editor": "^1.1.26", "@next/bundle-analyzer": "^9.4.4", "@sentry/browser": "5.17.0", "@sentry/node": "5.17.0", diff --git a/src/containers/editor/RichEditor/styles/overwrite.ts b/src/containers/editor/RichEditor/styles/overwrite.ts index 174eda634..ff70a3ae4 100644 --- a/src/containers/editor/RichEditor/styles/overwrite.ts +++ b/src/containers/editor/RichEditor/styles/overwrite.ts @@ -22,6 +22,9 @@ const RichEditorStyle = createGlobalStyle` .codex-editor svg { // vertical-align: super !important; } + .ce-settings__button--delete:hover .icon { + fill: tomato !important; + } .ce-header-wrapper h1:hover::after, .ce-header-wrapper h2:hover::after, From c415a687a3f476e359f967eeca0666b0d85dd606 Mon Sep 17 00:00:00 2001 From: mydearxym Date: Sun, 10 Oct 2021 16:37:36 +0800 Subject: [PATCH 6/8] refactor(upvote): enhance latestUser in upvote actions --- src/components/ArticleCard/Footer.tsx | 23 ++++++++++--- src/components/AvatarsRow/index.tsx | 4 +-- src/components/AvatarsRow/styles/index.ts | 1 - src/components/Cards/UserCard.tsx | 2 +- src/containers/thread/ArticlesThread/logic.ts | 17 +++++++--- src/containers/thread/ArticlesThread/store.ts | 8 ++++- src/schemas/fragments/base.ts | 34 +++++++++++++++++-- src/spec/article.ts | 2 +- src/spec/index.ts | 3 +- 9 files changed, 77 insertions(+), 17 deletions(-) diff --git a/src/components/ArticleCard/Footer.tsx b/src/components/ArticleCard/Footer.tsx index a49cb6cc6..8e37a0c2f 100644 --- a/src/components/ArticleCard/Footer.tsx +++ b/src/components/ArticleCard/Footer.tsx @@ -1,7 +1,8 @@ import { FC, memo } from 'react' import TimeAgo from 'timeago-react' -import type { TJob } from '@/spec' +import type { TArticle } from '@/spec' +import { upvoteOnArticleList } from '@/utils/helper' import { ICON } from '@/config' import Upvote from '@/components/Upvote' import DotDivider from '@/components/DotDivider' @@ -10,11 +11,18 @@ import IconText from '@/components/IconText' import { Wrapper, PublishWrapper, Bottom } from './styles/footer' type TProps = { - data: TJob + data: TArticle } const Footer: FC = ({ data }) => { - const { author, insertedAt, commentsCount, upvotesCount, meta } = data + const { + author, + insertedAt, + commentsCount, + upvotesCount, + viewerHasUpvoted, + meta, + } = data return ( @@ -23,7 +31,14 @@ const Footer: FC = ({ data }) => { - + + upvoteOnArticleList(data, viewerHasUpvoted) + } + /> {commentsCount} diff --git a/src/components/AvatarsRow/index.tsx b/src/components/AvatarsRow/index.tsx index d7223a8d1..f027af03f 100755 --- a/src/components/AvatarsRow/index.tsx +++ b/src/components/AvatarsRow/index.tsx @@ -91,7 +91,7 @@ const AvatarsRow: FC = ({ onTotalSelect = log, showMore = true, showTotalNumber = false, - reverse = true, + reverse = false, // see https://github.com/Aljullu/react-lazy-load-image-component/issues/42 scrollPosition = null, popCardPlacement = 'bottom', @@ -103,7 +103,7 @@ const AvatarsRow: FC = ({ const totalCount = total || users.length users = filter(validUser, getUniqueArray(users, 'login')) - const sortedUsers = reverse ? users : reverseFn(users) + const sortedUsers = reverse ? reverseFn(users) : users return ( diff --git a/src/components/AvatarsRow/styles/index.ts b/src/components/AvatarsRow/styles/index.ts index 0cba325b2..f2fb40977 100755 --- a/src/components/AvatarsRow/styles/index.ts +++ b/src/components/AvatarsRow/styles/index.ts @@ -18,7 +18,6 @@ export const Wrapper = styled.ul<{ total: number }>` ` export const AvatarsWrapper = styled.div` ${css.flex()} - flex-direction: row-reverse; ` export const TotalOneOffset = styled.span` margin-right: 10px; diff --git a/src/components/Cards/UserCard.tsx b/src/components/Cards/UserCard.tsx index a1e44c9f3..72a461f80 100644 --- a/src/components/Cards/UserCard.tsx +++ b/src/components/Cards/UserCard.tsx @@ -31,7 +31,7 @@ const UserCard: FC = ({ item }) => { - <Nickname>{nickname}</Nickname> + <Nickname>{cutRest(nickname, 12)}</Nickname> <Login>{login}</Login> {/* {bio || '--'} */} diff --git a/src/containers/thread/ArticlesThread/logic.ts b/src/containers/thread/ArticlesThread/logic.ts index 03a20bbc7..db868891b 100755 --- a/src/containers/thread/ArticlesThread/logic.ts +++ b/src/containers/thread/ArticlesThread/logic.ts @@ -1,4 +1,5 @@ import { useEffect } from 'react' +import { includes } from 'ramda' import type { TArticle, TThread, TArticleFilter } from '@/spec' import { TYPE, EVENT, ERR, THREAD } from '@/constant' @@ -11,6 +12,7 @@ import { matchPagedArticles, matchArticleUpvotes } from '@/utils/macros' import type { TStore } from './store' import S from './schema' +import { threadOnChange } from '@/containers/user/UserPublishedComments/logic' /* eslint-disable-next-line */ const log = buildLog('L:ArticlesThread') @@ -72,17 +74,24 @@ const handleUpvote = (article: TArticle, viewerHasUpvoted: boolean): void => { const { id, meta } = article store.updateUpvote(id, viewerHasUpvoted) + const queryLatestUsers = includes(article.meta.thread.toLowerCase(), [ + THREAD.RADAR, + THREAD.JOB, + ]) viewerHasUpvoted - ? sr71$.mutate(S.getUpvoteSchema(meta.thread), { id }) - : sr71$.mutate(S.getUndoUpvoteSchema(meta.thread), { id }) + ? sr71$.mutate(S.getUpvoteSchema(meta.thread, queryLatestUsers), { id }) + : sr71$.mutate(S.getUndoUpvoteSchema(meta.thread, queryLatestUsers), { id }) } -const handleUovoteRes = ({ id, upvotesCount }) => - store.updateUpvoteCount(id, upvotesCount) +const handleUovoteRes = ({ id, upvotesCount, meta }) => { + log('# handleUovoteRes: ', meta) + store.updateUpvoteCount(id, upvotesCount, meta) +} const handlePagedArticlesRes = (thread: TThread, pagedArticles): void => { const key = `paged${titleCase(thread)}s` + log('pagedArticles -> ', pagedArticles) store.markRes({ [key]: pagedArticles }) } diff --git a/src/containers/thread/ArticlesThread/store.ts b/src/containers/thread/ArticlesThread/store.ts index baee498a3..24e25b15d 100755 --- a/src/containers/thread/ArticlesThread/store.ts +++ b/src/containers/thread/ArticlesThread/store.ts @@ -11,6 +11,7 @@ import type { TTag, TAccount, TArticle, + TArticleMeta, TPagedArticles, TCommunity, TThread, @@ -172,13 +173,18 @@ const ArticlesThread = T.model('ArticlesThread', { self[pagedArticleKey].entries[index].upvotesCount = curCount self[pagedArticleKey].entries[index].viewerHasUpvoted = viewerHasUpvoted }, - updateUpvoteCount(id: TID, count: number): void { + updateUpvoteCount(id: TID, count: number, meta: TArticleMeta): void { const slf = self as TStore const { pagedArticleKey } = slf const index = slf.targetArticleIndex(id) if (index === null) return + if (meta) { + slf[pagedArticleKey].entries[index].meta.latestUpvotedUsers = + meta.latestUpvotedUsers + } + slf[pagedArticleKey].entries[index].upvotesCount = count }, markRoute(params): void { diff --git a/src/schemas/fragments/base.ts b/src/schemas/fragments/base.ts index a261f8136..700a4c7db 100755 --- a/src/schemas/fragments/base.ts +++ b/src/schemas/fragments/base.ts @@ -250,7 +250,22 @@ export const pagedCounts = ` pageNumber ` -export const getUpvoteSchema = (thread) => { +export const getUpvoteSchema = (thread, withLatestUser = true) => { + if (withLatestUser) { + return gql` + mutation ($id: ID!) { + upvote${titleCase(thread)}(id: $id) { + id + upvotesCount + meta { + latestUpvotedUsers { + ${author} + } + } + } + } + ` + } return gql` mutation ($id: ID!) { upvote${titleCase(thread)}(id: $id) { @@ -261,7 +276,22 @@ export const getUpvoteSchema = (thread) => { ` } -export const getUndoUpvoteSchema = (thread) => { +export const getUndoUpvoteSchema = (thread, withLatestUser = true) => { + if (withLatestUser) { + return gql` + mutation ($id: ID!) { + undoUpvote${titleCase(thread)}(id: $id) { + id + upvotesCount + meta { + latestUpvotedUsers { + ${author} + } + } + } + } + ` + } return gql` mutation ($id: ID!) { undoUpvote${titleCase(thread)}(id: $id) { diff --git a/src/spec/article.ts b/src/spec/article.ts index c97d76c0d..83faef1b7 100644 --- a/src/spec/article.ts +++ b/src/spec/article.ts @@ -5,7 +5,7 @@ import type { TEmotion } from './emotion' export type TCopyright = 'cc' | 'approve' | 'forbid' -type TArticleMeta = { +export type TArticleMeta = { thread?: string citingCount?: number isCommentLocked?: boolean diff --git a/src/spec/index.ts b/src/spec/index.ts index 91bc721ed..ad011e389 100644 --- a/src/spec/index.ts +++ b/src/spec/index.ts @@ -1,5 +1,5 @@ import type { TRootStore as RootStoreType } from '@/stores/RootStore' -import type { TArticle } from './article' +import type { TArticle, TArticleMeta } from './article' import type { TCommunity } from './community' export type { TMetric } from './metric' @@ -66,6 +66,7 @@ export type { TPagedCollectionFolder, TDocument, TArticle, + TArticleMeta, TPost, TBlog, TBlogRSS, From 8d3e6c3aa947edb249671011de5355a2f39f0f11 Mon Sep 17 00:00:00 2001 From: mydearxym Date: Sun, 10 Oct 2021 18:05:15 +0800 Subject: [PATCH 7/8] refactor(upvote): enhance latestUser in viewer --- src/containers/thread/ArticlesThread/store.ts | 6 +++--- src/containers/viewer/ArticleViewer/logic.ts | 21 ++++++++++++------- src/containers/viewer/ArticleViewer/store.ts | 5 +++-- src/schemas/fragments/base.ts | 4 ++-- src/stores/ViewingStore/index.ts | 17 +++++++++++---- 5 files changed, 35 insertions(+), 18 deletions(-) diff --git a/src/containers/thread/ArticlesThread/store.ts b/src/containers/thread/ArticlesThread/store.ts index 24e25b15d..eea317dc5 100755 --- a/src/containers/thread/ArticlesThread/store.ts +++ b/src/containers/thread/ArticlesThread/store.ts @@ -150,14 +150,14 @@ const ArticlesThread = T.model('ArticlesThread', { const root = getParent(self) as TRootStore root.setViewing(sobj) }, - updateArticle(article: TArticle): void { + updateArticle(fields: TArticle): void { const slf = self as TStore const { pagedArticleKey } = slf - const index = slf.targetArticleIndex(article.id) + const index = slf.targetArticleIndex(fields.id) if (index === null) return const targetArticle = toJS(slf[pagedArticleKey].entries[index]) - slf[pagedArticleKey].entries[index] = merge(targetArticle, article) + slf[pagedArticleKey].entries[index] = merge(targetArticle, fields) }, updateUpvote(id: TID, viewerHasUpvoted: boolean): void { const slf = self as TStore diff --git a/src/containers/viewer/ArticleViewer/logic.ts b/src/containers/viewer/ArticleViewer/logic.ts index cbb15e85c..1057b39c9 100755 --- a/src/containers/viewer/ArticleViewer/logic.ts +++ b/src/containers/viewer/ArticleViewer/logic.ts @@ -1,5 +1,5 @@ import { useEffect } from 'react' -import { merge } from 'ramda' +import { merge, includes } from 'ramda' import type { TArticle } from '@/spec' @@ -33,9 +33,11 @@ export const handleUpvote = ( store.updateUpvote(viewerHasUpvoted) + const queryLatestUsers = true + viewerHasUpvoted - ? sr71$.mutate(S.getUpvoteSchema(meta.thread), { id }) - : sr71$.mutate(S.getUndoUpvoteSchema(meta.thread), { id }) + ? sr71$.mutate(S.getUpvoteSchema(meta.thread, queryLatestUsers), { id }) + : sr71$.mutate(S.getUndoUpvoteSchema(meta.thread, queryLatestUsers), { id }) } const loadArticle = (): void => { @@ -66,11 +68,16 @@ const handleArticleRes = (article: TArticle): void => { }) } -const handleUovoteRes = ({ upvotesCount }) => { - store.updateUpvoteCount(upvotesCount) +const handleUovoteRes = ({ upvotesCount, meta }) => { + store.updateUpvoteCount(upvotesCount, meta) - const { id, viewerHasUpvoted } = store.viewingArticle - store.syncArticle({ id, viewerHasUpvoted, upvotesCount }) + const { + id, + viewerHasUpvoted, + meta: viewingArticleMeta, + } = store.viewingArticle + const syncMeta = merge(viewingArticleMeta, meta) + store.syncArticle({ id, viewerHasUpvoted, upvotesCount, meta: syncMeta }) } // ############################### diff --git a/src/containers/viewer/ArticleViewer/store.ts b/src/containers/viewer/ArticleViewer/store.ts index 7ff2ca5fc..b1c36bb7b 100755 --- a/src/containers/viewer/ArticleViewer/store.ts +++ b/src/containers/viewer/ArticleViewer/store.ts @@ -10,6 +10,7 @@ import type { TRootStore, TAccount, TArticle, + TArticleMeta, TThread, } from '@/spec' import { markStates, toJS } from '@/utils/mobx' @@ -58,9 +59,9 @@ const ArticleViewer = T.model('ArticleViewer', { const root = getParent(self) as TRootStore return root.viewing.updateUpvote(viewerHasUpvoted) }, - updateUpvoteCount(count: number): void { + updateUpvoteCount(count: number, meta: TArticleMeta): void { const root = getParent(self) as TRootStore - return root.viewing.updateUpvoteCount(count) + return root.viewing.updateUpvoteCount(count, meta) }, mark(sobj: Record): void { markStates(sobj, self) diff --git a/src/schemas/fragments/base.ts b/src/schemas/fragments/base.ts index 700a4c7db..5172a0ee6 100755 --- a/src/schemas/fragments/base.ts +++ b/src/schemas/fragments/base.ts @@ -250,7 +250,7 @@ export const pagedCounts = ` pageNumber ` -export const getUpvoteSchema = (thread, withLatestUser = true) => { +export const getUpvoteSchema = (thread, withLatestUser = false) => { if (withLatestUser) { return gql` mutation ($id: ID!) { @@ -276,7 +276,7 @@ export const getUpvoteSchema = (thread, withLatestUser = true) => { ` } -export const getUndoUpvoteSchema = (thread, withLatestUser = true) => { +export const getUndoUpvoteSchema = (thread, withLatestUser = false) => { if (withLatestUser) { return gql` mutation ($id: ID!) { diff --git a/src/stores/ViewingStore/index.ts b/src/stores/ViewingStore/index.ts index e2110d69f..72d8d1a93 100755 --- a/src/stores/ViewingStore/index.ts +++ b/src/stores/ViewingStore/index.ts @@ -6,7 +6,14 @@ import { types as T, getParent, Instance } from 'mobx-state-tree' import { values, merge, includes } from 'ramda' -import type { TRootStore, TUser, TArticle, TThread, TAccount } from '@/spec' +import type { + TRootStore, + TUser, + TArticle, + TArticleMeta, + TThread, + TAccount, +} from '@/spec' import { THREAD, ARTICLE_THREAD } from '@/constant' import { markStates } from '@/utils/mobx' import { User, Community, Post, Blog, Job, Radar, Works } from '@/model' @@ -77,11 +84,13 @@ const ViewingStore = T.model('ViewingStore', { self[currentThread].viewerHasUpvoted = false } }, - updateUpvoteCount(count: number): void { + updateUpvoteCount(count: number, meta?: TArticleMeta): void { const { currentThread } = self as TStore - if (self[currentThread].upvotesCount !== count) { - self[currentThread].upvotesCount = count + if (meta) { + self[currentThread].meta.latestUpvotedUsers = meta.latestUpvotedUsers } + + self[currentThread].upvotesCount = count }, updateViewingIfNeed(type, sobj): void { const { updateViewingUser } = self as TStore From d7cf6b0220b994bf6551c802b4580e27eddf3bbd Mon Sep 17 00:00:00 2001 From: mydearxym Date: Sun, 10 Oct 2021 21:53:43 +0800 Subject: [PATCH 8/8] chore(clean-up): unused container & code --- src/containers/digest/ArticleDigest/logic.ts | 71 +------- src/containers/digest/ArticleDigest/schema.ts | 19 +-- src/containers/tool/CollectionFolder/logic.ts | 2 - .../unit/ArticleViewerHeader/CompanyInfo.js | 43 ----- .../ArticleViewerHeader/FavoriteReaction.js | 72 --------- .../unit/ArticleViewerHeader/LastSyncInfo.js | 56 ------- .../unit/ArticleViewerHeader/StarReaction.js | 69 -------- .../unit/ArticleViewerHeader/UserInfo.js | 24 --- .../unit/ArticleViewerHeader/ViewCounter.js | 28 ---- .../unit/ArticleViewerHeader/index.js | 96 ----------- .../unit/ArticleViewerHeader/logic.js | 152 ------------------ .../unit/ArticleViewerHeader/schema.ts | 52 ------ .../unit/ArticleViewerHeader/store.js | 61 ------- .../styles/company_info.ts | 44 ----- .../unit/ArticleViewerHeader/styles/index.ts | 17 -- .../ArticleViewerHeader/styles/reaction.ts | 77 --------- .../ArticleViewerHeader/styles/user_info.ts | 21 --- .../ArticleViewerHeader/tests/index.test.ts | 10 -- .../ArticleViewerHeader/tests/store.test.ts | 10 -- src/containers/viewer/ArticleViewer/index.tsx | 3 - src/containers/viewer/ArticleViewer/logic.ts | 2 +- utils/constant/event.ts | 1 - 22 files changed, 8 insertions(+), 922 deletions(-) delete mode 100755 src/containers/unit/ArticleViewerHeader/CompanyInfo.js delete mode 100755 src/containers/unit/ArticleViewerHeader/FavoriteReaction.js delete mode 100755 src/containers/unit/ArticleViewerHeader/LastSyncInfo.js delete mode 100755 src/containers/unit/ArticleViewerHeader/StarReaction.js delete mode 100755 src/containers/unit/ArticleViewerHeader/UserInfo.js delete mode 100755 src/containers/unit/ArticleViewerHeader/ViewCounter.js delete mode 100755 src/containers/unit/ArticleViewerHeader/index.js delete mode 100755 src/containers/unit/ArticleViewerHeader/logic.js delete mode 100755 src/containers/unit/ArticleViewerHeader/schema.ts delete mode 100755 src/containers/unit/ArticleViewerHeader/store.js delete mode 100755 src/containers/unit/ArticleViewerHeader/styles/company_info.ts delete mode 100755 src/containers/unit/ArticleViewerHeader/styles/index.ts delete mode 100755 src/containers/unit/ArticleViewerHeader/styles/reaction.ts delete mode 100755 src/containers/unit/ArticleViewerHeader/styles/user_info.ts delete mode 100755 src/containers/unit/ArticleViewerHeader/tests/index.test.ts delete mode 100755 src/containers/unit/ArticleViewerHeader/tests/store.test.ts diff --git a/src/containers/digest/ArticleDigest/logic.ts b/src/containers/digest/ArticleDigest/logic.ts index 3fede249e..cb7c62f9b 100755 --- a/src/containers/digest/ArticleDigest/logic.ts +++ b/src/containers/digest/ArticleDigest/logic.ts @@ -1,23 +1,20 @@ import { useEffect } from 'react' -import { merge, toUpper } from 'ramda' +import { merge } from 'ramda' import type { TScrollDirection } from '@/spec' -import { TYPE, EVENT, ERR, THREAD } from '@/constant' +import { EVENT, ERR } from '@/constant' import asyncSuit from '@/utils/async' import { send, errRescue } from '@/utils/helper' import { buildLog } from '@/utils/logger' import type { TStore } from './store' -import S from './schema' +// import S from './schema' /* eslint-disable-next-line */ const log = buildLog('L:ArticleDigest') const { SR71, $solver, asyncRes, asyncErr } = asyncSuit -const sr71$ = new SR71({ - /* @ts-ignore */ - receive: [EVENT.REFRESH_REACTIONS], -}) +const sr71$ = new SR71() let sub$ = null let store = null @@ -30,48 +27,12 @@ export const outAnchor = (): void => { if (store) store.mark({ inViewport: false }) } -export const onReaction = (action, userDid, { id }): void => { - if (!store.isLogin) return store.authWarning() - if (store.loading) return - const thread = store.activeThread - - store.mark({ action }) - /* log('onReaction thread: ', thread) */ - if (action === TYPE.FAVORITE) { - // call favoriteSetter - return send(EVENT.SET_FAVORITE_CONTENT, { - data: { thread }, - }) - } - - markLoading(true) - const args = { id, thread: toUpper(thread), action } - - return userDid - ? sr71$.mutate(S.undoReaction, args) - : sr71$.mutate(S.reaction, args) -} - export const onListReactionUsers = (type, data): void => send(EVENT.USER_LISTER_OPEN, { type, data: { ...data, thread: store.activeThread }, }) -const afterReaction = (id) => { - const thread = store.activeThread - switch (thread) { - case THREAD.JOB: - return sr71$.query(S.job, { id }) - - case THREAD.REPO: - return sr71$.query(S.repo, { id }) - - default: - return sr71$.query(S.post, { id }) - } -} - const markLoading = (maybe = true) => store.mark({ loading: maybe }) // ############################### @@ -95,30 +56,6 @@ const DataSolver = [ markLoading(false) }, }, - { - match: asyncRes('repo'), - action: ({ repo }) => { - store.setViewing({ repo: merge(store.viewingArticle, repo) }) - store.syncArticle(repo) - markLoading(false) - }, - }, - { - match: asyncRes('reaction'), - action: ({ reaction: { id } }) => afterReaction(id), - }, - { - match: asyncRes('undoReaction'), - action: ({ undoReaction: { id } }) => afterReaction(id), - }, - { - match: asyncRes(EVENT.REFRESH_REACTIONS), - action: (e) => { - markLoading(true) - const { id } = e[EVENT.REFRESH_REACTIONS].data - afterReaction(id) - }, - }, ] const ErrSolver = [ { diff --git a/src/containers/digest/ArticleDigest/schema.ts b/src/containers/digest/ArticleDigest/schema.ts index 47d68ebc3..c23e0086c 100755 --- a/src/containers/digest/ArticleDigest/schema.ts +++ b/src/containers/digest/ArticleDigest/schema.ts @@ -1,15 +1,7 @@ import { gql } from '@urql/core' -import { P } from '@/schemas' - -const reaction = gql` - ${P.reaction} -` -const undoReaction = gql` - ${P.undoReaction} -` const post = gql` - query($id: ID!) { + query ($id: ID!) { post(id: $id) { id commentsCount @@ -17,25 +9,22 @@ const post = gql` upvotesCount viewerHasCollected viewerHasUpvoted - favoritedCategoryId } } ` const job = gql` - query($id: ID!) { + query ($id: ID!) { job(id: $id) { id collectsCount viewerHasCollected - favoritedCategoryId } } ` const repo = gql` - query($id: ID!) { + query ($id: ID!) { repo(id: $id) { id - favoritedCategoryId viewerHasCollected collectsCount } @@ -43,8 +32,6 @@ const repo = gql` ` const schema = { - reaction, - undoReaction, post, job, repo, diff --git a/src/containers/tool/CollectionFolder/logic.ts b/src/containers/tool/CollectionFolder/logic.ts index 8bc9893fa..1264db1ee 100755 --- a/src/containers/tool/CollectionFolder/logic.ts +++ b/src/containers/tool/CollectionFolder/logic.ts @@ -176,7 +176,6 @@ const DataSolver = [ /* store.updateCategory(cat) */ const { id } = store.viewingArticle const { thread } = store - send(EVENT.REFRESH_REACTIONS, { data: { id, thread } }) store.mark({ doing: false }) }, }, @@ -187,7 +186,6 @@ const DataSolver = [ /* store.updateCategory(cat) */ const { id } = store.viewingArticle const { thread } = store - send(EVENT.REFRESH_REACTIONS, { data: { id, thread } }) store.mark({ doing: false }) }, }, diff --git a/src/containers/unit/ArticleViewerHeader/CompanyInfo.js b/src/containers/unit/ArticleViewerHeader/CompanyInfo.js deleted file mode 100755 index 5da44ee33..000000000 --- a/src/containers/unit/ArticleViewerHeader/CompanyInfo.js +++ /dev/null @@ -1,43 +0,0 @@ -import React from 'react' -import TimeAgo from 'timeago-react' - -import DotDivider from '@/components/DotDivider' -import { ICON_CMD } from '@/config' - -import { cutRest } from '@/utils/helper' -import { - Wrapper, - Logo, - Title, - PublishAt, - Username, - HomePage, - HomeIcon, - HomepageLink, -} from './styles/company_info' - -const CompanyInfo = ({ company, insertedAt, author }) => ( - - -
- {cutRest(company.title, 14)} - - - - {company.link || '--'} - - - - {author?.nickname} - - 发布于: - -
-
-) - -export default React.memo(CompanyInfo) diff --git a/src/containers/unit/ArticleViewerHeader/FavoriteReaction.js b/src/containers/unit/ArticleViewerHeader/FavoriteReaction.js deleted file mode 100755 index 61d806668..000000000 --- a/src/containers/unit/ArticleViewerHeader/FavoriteReaction.js +++ /dev/null @@ -1,72 +0,0 @@ -import React from 'react' -import T from 'prop-types' -import { values } from 'ramda' - -import { ICON_CMD } from '@/config' -import { TYPE, THREAD } from '@/constant' - -import Maybe from '@/components/Maybe' -import { - Reaction, - ReactionAction, - ReactionName, - ReactionLoading, - CollectIcon, - ReactionUserNum, - Divider, -} from './styles/reaction' - -import { onReaction, onListReactionUsers } from './logic' - -const FavoriteReaction = ({ data, thread, show, loading }) => ( - - - onReaction(TYPE.FAVORITE, data.viewerHasCollected, data)} - > - - - {data.viewerHasCollected ? 已收藏 : 收藏} - - - {loading ? ( - - ) : ( - - onListReactionUsers(TYPE.USER_LISTER_FAVORITES, { - thread, - id: data.id, - action: TYPE.FAVORITE, - brief: data.title || '', - }) - } - > - {data.collectsCount} - - )} - - - -) - -FavoriteReaction.propTypes = { - thread: T.oneOf(values(THREAD)), - data: T.shape({ - id: T.string, - title: T.string, - viewerHasCollected: T.bool, - collectsCount: T.number, - }).isRequired, - show: T.bool, - loading: T.bool, -} - -FavoriteReaction.defaultProps = { - thread: THREAD.POST, - show: true, - loading: false, -} - -export default React.memo(FavoriteReaction) diff --git a/src/containers/unit/ArticleViewerHeader/LastSyncInfo.js b/src/containers/unit/ArticleViewerHeader/LastSyncInfo.js deleted file mode 100755 index 3dde6b6bf..000000000 --- a/src/containers/unit/ArticleViewerHeader/LastSyncInfo.js +++ /dev/null @@ -1,56 +0,0 @@ -import React from 'react' -import T from 'prop-types' -import TimeAgo from 'timeago-react' - -import Maybe from '@/components/Maybe' -import Tooltip from '@/components/Tooltip' - -import { - Reaction, - PlainAction, - ReactionName, - SyncTime, - PopInfo, - Divider, -} from './styles/reaction' - -const LastSyncInfo = ({ show, data }) => { - const lastSyncTime = data.lastSync || data.updatedAt || null - - return ( - - 上次与该 Github repo 同步的时间} - > - - - - 同步于: - - - {lastSyncTime ? ( - - ) : ( - '--' - )} - - - - - ) -} - -LastSyncInfo.propTypes = { - data: T.shape({ - lastSync: T.string, - updatedAt: T.string, - }).isRequired, - show: T.bool, -} - -LastSyncInfo.defaultProps = { - show: true, -} - -export default React.memo(LastSyncInfo) diff --git a/src/containers/unit/ArticleViewerHeader/StarReaction.js b/src/containers/unit/ArticleViewerHeader/StarReaction.js deleted file mode 100755 index 3f8575594..000000000 --- a/src/containers/unit/ArticleViewerHeader/StarReaction.js +++ /dev/null @@ -1,69 +0,0 @@ -import React from 'react' -import T from 'prop-types' - -import { ICON_CMD } from '@/config' -import { TYPE } from '@/constant' - -import Maybe from '@/components/Maybe' -import { - Reaction, - ReactionAction, - ReactionName, - ReactionLoading, - LikeIcon, - ReactionUserNum, - Divider, -} from './styles/reaction' - -import { onReaction, onListReactionUsers } from './logic' - -const StarReaction = ({ data, show, loading }) => ( - - - onReaction(TYPE.STAR, data.viewerHasUpvoted, data)} - > - - - {data.viewerHasUpvoted ? 已赞 : } - - - {loading ? ( - - ) : ( - - onListReactionUsers(TYPE.USER_LISTER_STARS, { - id: data.id, - action: TYPE.STAR, - brief: data.title || '', - }) - } - > - {data.upvotesCount} - - )} - - - - -) - -StarReaction.propTypes = { - data: T.shape({ - id: T.string, - title: T.string, - viewerHasUpvoted: T.bool, - upvotesCount: T.number, - }).isRequired, - show: T.bool, - loading: T.bool, -} - -StarReaction.defaultProps = { - show: true, - loading: false, -} - -export default React.memo(StarReaction) diff --git a/src/containers/unit/ArticleViewerHeader/UserInfo.js b/src/containers/unit/ArticleViewerHeader/UserInfo.js deleted file mode 100755 index d7f6d1750..000000000 --- a/src/containers/unit/ArticleViewerHeader/UserInfo.js +++ /dev/null @@ -1,24 +0,0 @@ -import React from 'react' -import TimeAgo from 'timeago-react' - -import ImgFallback from '@/components/ImgFallback' -import { Wrapper, Avatar, UserName, PublishAt } from './styles/user_info' - -const UserInfo = ({ author, insertedAt }) => ( - - } - visibleByDefault - /> -
- {author.nickname} - - 发布于: - -
-
-) - -export default React.memo(UserInfo) diff --git a/src/containers/unit/ArticleViewerHeader/ViewCounter.js b/src/containers/unit/ArticleViewerHeader/ViewCounter.js deleted file mode 100755 index 36c8b977b..000000000 --- a/src/containers/unit/ArticleViewerHeader/ViewCounter.js +++ /dev/null @@ -1,28 +0,0 @@ -import React from 'react' -import T from 'prop-types' - -import { - Reaction, - PlainAction, - ReactionName, - PlainUserNum, -} from './styles/reaction' - -const ViewCounter = ({ data }) => ( - - - 浏览: - - {data.views} - -) - -ViewCounter.propTypes = { - data: T.shape({ - views: T.number, - }).isRequired, -} - -ViewCounter.defaultProps = {} - -export default React.memo(ViewCounter) diff --git a/src/containers/unit/ArticleViewerHeader/index.js b/src/containers/unit/ArticleViewerHeader/index.js deleted file mode 100755 index 739e69777..000000000 --- a/src/containers/unit/ArticleViewerHeader/index.js +++ /dev/null @@ -1,96 +0,0 @@ -/* - * - * ArticleViewerHeader - * - */ -import React from 'react' -import T from 'prop-types' -import { values } from 'ramda' - -import { THREAD } from '@/constant' -import { buildLog } from '@/utils/logger' -import { pluggedIn } from '@/utils/mobx' - -import Maybe from '@/components/Maybe' - -import UserInfo from './UserInfo' -import CompanyInfo from './CompanyInfo' -import FavoriteReaction from './FavoriteReaction' -import StarReaction from './StarReaction' -import ViewCounter from './ViewCounter' -import LastSyncInfo from './LastSyncInfo' - -import { Wrapper, ReactionWrapper } from './styles' -import { useInit } from './logic' - -/* eslint-disable-next-line */ -const log = buildLog('C:ArticleViewerHeader') - -const ArticleViewerHeaderContainer = ({ - articleViewerHeader: store, - thread, - author, - company, - data, - showFavorite, - showLastSync, - showStar, -}) => { - useInit(store) - - const { starLoading, favoriteLoading } = store - - return ( - - - - - - - - - - - - - - - ) -} - -ArticleViewerHeaderContainer.propTypes = { - articleViewerHeader: T.any.isRequired, - thread: T.oneOf(values(THREAD)), - author: T.object, - company: T.any, - data: T.any, - showFavorite: T.bool, - showLastSync: T.bool, - showStar: T.bool, -} - -ArticleViewerHeaderContainer.defaultProps = { - thread: THREAD.POST, - author: null, - company: null, - data: {}, - showStar: true, - showFavorite: true, - showLastSync: false, -} - -export default pluggedIn(ArticleViewerHeaderContainer) diff --git a/src/containers/unit/ArticleViewerHeader/logic.js b/src/containers/unit/ArticleViewerHeader/logic.js deleted file mode 100755 index 46647f669..000000000 --- a/src/containers/unit/ArticleViewerHeader/logic.js +++ /dev/null @@ -1,152 +0,0 @@ -import { useEffect } from 'react' -import { toUpper, merge } from 'ramda' - -import { TYPE, EVENT, ERR, THREAD } from '@/constant' - -import asyncSuit from '@/utils/async' -import { send, errRescue } from '@/utils/helper' -import { buildLog } from '@/utils/logger' - -import S from './schema' - -/* eslint-disable-next-line */ -const log = buildLog('L:ArticleViewerHeader') - -// EVENT.REFRESH_REACTIONS handles FAVORITE action when -// user set it from FavoriteSetter -const { SR71, $solver, asyncRes, asyncErr } = asyncSuit -const sr71$ = new SR71({ - receive: [EVENT.REFRESH_REACTIONS], -}) - -let sub$ = null -let store = null - -export const onReaction = (action, userDid, { id }) => { - if (!store.isLogin) return store.authWarning() - if (store.loading) return false - - const thread = store.activeThread - - store.mark({ action }) - /* log('onReaction thread: ', thread) */ - if (action === TYPE.FAVORITE) { - // call favoriteSetter - return send(EVENT.SET_FAVORITE_CONTENT, { - data: { thread }, - }) - } - - markLoading(true) - const args = { id, thread: toUpper(thread), action } - - return userDid - ? sr71$.mutate(S.undoReaction, args) - : sr71$.mutate(S.reaction, args) -} - -export const onListReactionUsers = (type, data) => - send(EVENT.USER_LISTER_OPEN, { - type, - data: { ...data, thread: store.activeThread }, - }) - -const afterReaction = (id) => { - const thread = store.activeThread - switch (thread) { - case THREAD.JOB: - return sr71$.query(S.job, { id }) - - case THREAD.REPO: - return sr71$.query(S.repo, { id }) - - default: - return sr71$.query(S.post, { id }) - } -} - -const markLoading = (maybe = true) => store.mark({ loading: maybe }) - -// ############################### -// Data & Error handlers -// ############################### - -const DataSolver = [ - { - match: asyncRes('post'), - action: ({ post }) => { - store.setViewing({ post: merge(store.viewingArticle, post) }) - store.syncArticle(post) - markLoading(false) - }, - }, - { - match: asyncRes('job'), - action: ({ job }) => { - store.setViewing({ job: merge(store.viewingArticle, job) }) - store.syncArticle(job) - markLoading(false) - }, - }, - { - match: asyncRes('repo'), - action: ({ repo }) => { - store.setViewing({ repo: merge(store.viewingArticle, repo) }) - store.syncArticle(repo) - markLoading(false) - }, - }, - { - match: asyncRes('reaction'), - action: ({ reaction: { id } }) => afterReaction(id), - }, - { - match: asyncRes('undoReaction'), - action: ({ undoReaction: { id } }) => afterReaction(id), - }, - { - match: asyncRes(EVENT.REFRESH_REACTIONS), - action: (e) => { - markLoading(true) - const { id } = e[EVENT.REFRESH_REACTIONS].data - afterReaction(id) - }, - }, -] -const ErrSolver = [ - { - match: asyncErr(ERR.GRAPHQL), - action: () => markLoading(false), - }, - { - match: asyncErr(ERR.TIMEOUT), - action: ({ details }) => { - markLoading(false) - errRescue({ type: ERR.TIMEOUT, details, path: 'ArticleViewerHeader' }) - }, - }, - { - match: asyncErr(ERR.NETWORK), - action: () => { - markLoading(false) - errRescue({ type: ERR.NETWORK, path: 'ArticleViewerHeader' }) - }, - }, -] - -// ############################### -// init & uninit -// ############################### -export const useInit = (_store) => { - useEffect(() => { - store = _store - // log('effect init') - sub$ = sr71$.data().subscribe($solver(DataSolver, ErrSolver)) - - return () => { - // log('effect uninit') - sr71$.stop() - sub$.unsubscribe() - } - }, [_store]) -} diff --git a/src/containers/unit/ArticleViewerHeader/schema.ts b/src/containers/unit/ArticleViewerHeader/schema.ts deleted file mode 100755 index fe86fa340..000000000 --- a/src/containers/unit/ArticleViewerHeader/schema.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { gql } from '@urql/core' -import { P } from '@/schemas' - -const reaction = gql` - ${P.reaction} -` -const undoReaction = gql` - ${P.undoReaction} -` - -const post = gql` - query($id: ID!) { - post(id: $id) { - id - collectsCount - upvotesCount - viewerHasCollected - viewerHasUpvoted - favoritedCategoryId - } - } -` -const job = gql` - query($id: ID!) { - job(id: $id) { - id - collectsCount - viewerHasCollected - favoritedCategoryId - } - } -` -const repo = gql` - query($id: ID!) { - repo(id: $id) { - id - favoritedCategoryId - viewerHasCollected - collectsCount - } - } -` - -const schema = { - reaction, - undoReaction, - post, - job, - repo, -} - -export default schema diff --git a/src/containers/unit/ArticleViewerHeader/store.js b/src/containers/unit/ArticleViewerHeader/store.js deleted file mode 100755 index cc92c9207..000000000 --- a/src/containers/unit/ArticleViewerHeader/store.js +++ /dev/null @@ -1,61 +0,0 @@ -/* - * ArticleViewerHeader store - * - */ - -import { types as T, getParent } from 'mobx-state-tree' - -import { TYPE } from '@/constant' -import { markStates } from '@/utils/mobx' - -const ArticleViewerHeader = T.model('ArticleViewerHeader', { - loading: T.optional(T.boolean, false), - action: T.optional( - T.enumeration('action', [TYPE.FAVORITE, TYPE.STAR]), - TYPE.FAVORITE, - ), -}) - .views((self) => ({ - get root() { - return getParent(self) - }, - get isLogin() { - return self.root.account.isLogin - }, - get accountInfo() { - return self.root.account.accountInfo - }, - get viewingArticle() { - return self.root.viewingArticle - }, - get activeThread() { - const { activeThread } = self.root.viewing - return activeThread - }, - get starLoading() { - const { action, loading } = self - if (action === TYPE.STAR && loading) return true - return false - }, - get favoriteLoading() { - const { action, loading } = self - if (action === TYPE.FAVORITE && loading) return true - return false - }, - })) - .actions((self) => ({ - authWarning(options) { - self.root.authWarning(options) - }, - setViewing(sobj) { - self.root.setViewing(sobj) - }, - syncArticle(item) { - self.root.viewing.syncArticle(item) - }, - mark(sobj) { - markStates(sobj, self) - }, - })) - -export default ArticleViewerHeader diff --git a/src/containers/unit/ArticleViewerHeader/styles/company_info.ts b/src/containers/unit/ArticleViewerHeader/styles/company_info.ts deleted file mode 100755 index e49b40e8f..000000000 --- a/src/containers/unit/ArticleViewerHeader/styles/company_info.ts +++ /dev/null @@ -1,44 +0,0 @@ -import styled from 'styled-components' - -import Img from '@/Img' -import { theme } from '@/utils/themes' -import css from '@/utils/css' - -export const Wrapper = styled.div` - ${css.flexGrow('align-center')}; - margin-left: 4px; -` -export const Title = styled.div` - font-size: 1rem; - color: ${theme('thread.articleTitle')}; - margin-top: 2px; -` -export const HomePage = styled.div` - ${css.flex('align-center')}; -` -export const HomeIcon = styled(Img)` - fill: ${theme('thread.articleDigest')}; - ${css.size(15)}; -` -export const HomepageLink = styled.a` - color: ${theme('thread.articleDigest')}; - margin-left: 3px; - &:hover { - text-decoration: underline; - color: ${theme('thread.articleTitle')}; - } -` -export const Username = styled.div` - font-size: 0.9rem; -` -export const PublishAt = styled.div` - ${css.flex('align-center')}; - font-size: 0.8rem; - color: ${theme('thread.articleDigest')}; -` -export const Logo = styled(Img)` - border-radius: 3px; - width: 64px; - height: 64px; - margin-right: 12px; -` diff --git a/src/containers/unit/ArticleViewerHeader/styles/index.ts b/src/containers/unit/ArticleViewerHeader/styles/index.ts deleted file mode 100755 index 090fa31ba..000000000 --- a/src/containers/unit/ArticleViewerHeader/styles/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -import styled from 'styled-components' - -// import Img from '@/Img' -import css from '@/utils/css' - -export const Wrapper = styled.div` - ${css.flex('align-center')}; - - padding-left: 35px; - padding-right: 30px; - padding-top: 10px; - padding-bottom: 6px; -` -export const ReactionWrapper = styled.div` - ${css.flex('align-center')}; - margin-right: 5px; -` diff --git a/src/containers/unit/ArticleViewerHeader/styles/reaction.ts b/src/containers/unit/ArticleViewerHeader/styles/reaction.ts deleted file mode 100755 index 37d26eb9f..000000000 --- a/src/containers/unit/ArticleViewerHeader/styles/reaction.ts +++ /dev/null @@ -1,77 +0,0 @@ -import styled from 'styled-components' - -import type { TActive } from '@/spec' -import Img from '@/Img' -import { theme } from '@/utils/themes' -import css from '@/utils/css' -import animate from '@/utils/animations' - -export const Divider = styled.div` - border-right: 1px solid; - border-color: ${theme('banner.desc')}; - height: 15px; - margin-left: 8px; - opacity: 0.8; - margin-right: 6px; -` -export const Reaction = styled.div` - ${css.flex('align-center')}; -` -export const PlainAction = styled.div` - ${css.flex('align-center')}; - border-radius: 5px; -` -export const ReactionAction = styled(PlainAction)` - background-color: ${({ active }) => - active ? theme('article.reactionHoverBg') : ''}; - padding: ${({ active }) => (active ? '2px 5px' : '2px 3px')}; - &:hover { - cursor: pointer; - font-weight: bold; - background: ${theme('article.reactionHoverBg')}; - } -` -export const ReactionName = styled.div` - color: ${theme('article.reactionTitle')}; - font-size: 0.9rem; -` -export const PlainUserNum = styled.div` - color: ${theme('article.reactionTitle')}; - font-size: 1rem; - margin-left: 2px; -` -export const SyncTime = styled(PlainUserNum)` - font-size: 0.9rem; - margin-left: 3px; -` -export const PopInfo = styled.div` - padding: 5px 10px; - color: ${theme('article.reactionTitle')}; -` -export const ReactionUserNum = styled(PlainUserNum)` - &:hover { - cursor: pointer; - text-decoration: underline; - color: ${theme('contrastFg')}; - } -` -const ReactionIcon = styled(Img)` - fill: ${theme('article.reactionTitle')}; - ${css.size(24)}; - margin-right: 2px; -` - -export const ReactionLoading = styled(Img)` - fill: ${theme('article.reactionTitle')}; - ${css.size(18)}; - margin-right: 2px; - margin-left: 4px; - animation: ${animate.rotate360} 1s linear infinite; -` -export const CollectIcon = styled(ReactionIcon)` - margin-top: -2px; -` -export const LikeIcon = styled(ReactionIcon)` - margin-top: -5px; - ${css.size(22)}; -` diff --git a/src/containers/unit/ArticleViewerHeader/styles/user_info.ts b/src/containers/unit/ArticleViewerHeader/styles/user_info.ts deleted file mode 100755 index 3f0e43674..000000000 --- a/src/containers/unit/ArticleViewerHeader/styles/user_info.ts +++ /dev/null @@ -1,21 +0,0 @@ -import styled from 'styled-components' - -import Img from '@/Img' -import { theme } from '@/utils/themes' -import css from '@/utils/css' - -export const Wrapper = styled.div` - ${css.flexGrow('align-center')}; -` -export const UserName = styled.div` - font-size: 0.9rem; - color: ${theme('thread.articleTitle')}; -` -export const PublishAt = styled.div` - font-size: 0.8rem; - color: ${theme('thread.articleDigest')}; -` -export const Avatar = styled(Img)` - ${css.circle(35)}; - margin-right: 10px; -` diff --git a/src/containers/unit/ArticleViewerHeader/tests/index.test.ts b/src/containers/unit/ArticleViewerHeader/tests/index.test.ts deleted file mode 100755 index 78db754e5..000000000 --- a/src/containers/unit/ArticleViewerHeader/tests/index.test.ts +++ /dev/null @@ -1,10 +0,0 @@ -// import React from 'react' -// import { shallow } from 'enzyme' - -// import ArticleViewerHeader from '../index' - -describe('TODO ', () => { - it('Expect to have unit tests specified', () => { - expect(true).toEqual(true) - }) -}) diff --git a/src/containers/unit/ArticleViewerHeader/tests/store.test.ts b/src/containers/unit/ArticleViewerHeader/tests/store.test.ts deleted file mode 100755 index 4f8b4647e..000000000 --- a/src/containers/unit/ArticleViewerHeader/tests/store.test.ts +++ /dev/null @@ -1,10 +0,0 @@ -/* - * ArticleViewerHeader store test - * - */ - -// import ArticleViewerHeader from '../index' - -it('TODO: store test ArticleViewerHeader', () => { - expect(1 + 1).toBe(2) -}) diff --git a/src/containers/viewer/ArticleViewer/index.tsx b/src/containers/viewer/ArticleViewer/index.tsx index 4f381297f..28a57df25 100755 --- a/src/containers/viewer/ArticleViewer/index.tsx +++ b/src/containers/viewer/ArticleViewer/index.tsx @@ -11,9 +11,6 @@ import { pluggedIn } from '@/utils/mobx' import Comments from '@/containers/unit/Comments' -// TODO: remove -// import ArticleViewerHeader from '@/containers/unit/ArticleViewerHeader' - import Viewer from './Viewer' import type { TStore } from './store' diff --git a/src/containers/viewer/ArticleViewer/logic.ts b/src/containers/viewer/ArticleViewer/logic.ts index 1057b39c9..ca030aa4e 100755 --- a/src/containers/viewer/ArticleViewer/logic.ts +++ b/src/containers/viewer/ArticleViewer/logic.ts @@ -1,5 +1,5 @@ import { useEffect } from 'react' -import { merge, includes } from 'ramda' +import { merge } from 'ramda' import type { TArticle } from '@/spec' diff --git a/utils/constant/event.ts b/utils/constant/event.ts index be24ed62c..2e6e925b6 100755 --- a/utils/constant/event.ts +++ b/utils/constant/event.ts @@ -42,7 +42,6 @@ const EVENT = { REFRESH_POSTS: 'REFRESH_POSTS', REFRESH_REPOS: 'REFRESH_REPOS', REFRESH_JOBS: 'REFRESH_JOBS', - REFRESH_REACTIONS: 'REFRESH_REACTIONS', // sync repo SYNC_REPO: 'SYNC_REPO',