From 6be80fd8ba237c63129759169875c88ee2e021c4 Mon Sep 17 00:00:00 2001 From: RoberTu Date: Mon, 6 Jul 2020 18:56:54 +0800 Subject: [PATCH 01/86] feat(article-edit-mode): editing tags & collection of article --- src/common/enums/text.ts | 2 + .../DropdownActions/EditButton.tsx | 13 ++ .../ArticleDigest/DropdownActions/index.tsx | 4 + .../Editor}/Sidebar/Collapsable/index.tsx | 0 .../Editor}/Sidebar/Collapsable/styles.css | 0 .../CollectionEditor/CollectForm.tsx | 0 .../Collection}/CollectionEditor/index.tsx | 0 .../Collection}/CollectionEditor/styles.css | 0 .../Editor/Sidebar/Collection/index.tsx | 50 ++++++ .../Editor/Sidebar/Collection}/styles.css | 0 .../ArticleDetail/Collection/EditButton.tsx | 161 ------------------ .../ArticleDetail/Collection/EditingList.tsx | 82 --------- src/views/ArticleDetail/Collection/index.tsx | 41 +---- src/views/ArticleDetail/Collection/styles.css | 25 --- src/views/ArticleDetail/Content/index.tsx | 2 +- .../ArticleDetail/EditModeHeader/index.tsx | 35 ++++ .../ArticleDetail/EditModeHeader/styles.css | 5 + .../EditModeSidebar/CollectArticles.tsx | 118 +++++++++++++ .../ArticleDetail/EditModeSidebar/index.tsx | 27 +++ src/views/ArticleDetail/Toolbar/index.tsx | 11 +- src/views/ArticleDetail/index.tsx | 42 ++++- src/views/ArticleDetail/styles.css | 5 + .../Me/DraftDetail/Sidebar/AddCover/index.tsx | 6 +- .../Me/DraftDetail/Sidebar/AddTags/index.tsx | 9 +- .../Sidebar/CollectArticles/index.tsx | 58 ++----- 25 files changed, 335 insertions(+), 361 deletions(-) create mode 100644 src/components/ArticleDigest/DropdownActions/EditButton.tsx rename src/{views/Me/DraftDetail => components/Editor}/Sidebar/Collapsable/index.tsx (100%) rename src/{views/Me/DraftDetail => components/Editor}/Sidebar/Collapsable/styles.css (100%) rename src/components/{ => Editor/Sidebar/Collection}/CollectionEditor/CollectForm.tsx (100%) rename src/components/{ => Editor/Sidebar/Collection}/CollectionEditor/index.tsx (100%) rename src/components/{ => Editor/Sidebar/Collection}/CollectionEditor/styles.css (100%) create mode 100644 src/components/Editor/Sidebar/Collection/index.tsx rename src/{views/Me/DraftDetail/Sidebar/CollectArticles => components/Editor/Sidebar/Collection}/styles.css (100%) delete mode 100644 src/views/ArticleDetail/Collection/EditButton.tsx delete mode 100644 src/views/ArticleDetail/Collection/EditingList.tsx create mode 100644 src/views/ArticleDetail/EditModeHeader/index.tsx create mode 100644 src/views/ArticleDetail/EditModeHeader/styles.css create mode 100644 src/views/ArticleDetail/EditModeSidebar/CollectArticles.tsx create mode 100644 src/views/ArticleDetail/EditModeSidebar/index.tsx diff --git a/src/common/enums/text.ts b/src/common/enums/text.ts index 105c3a701b..f72305c8fc 100644 --- a/src/common/enums/text.ts +++ b/src/common/enums/text.ts @@ -71,6 +71,7 @@ export const TEXT = { draft: '草稿', DUPLICATE_TAG: '標籤名稱已被使用', edit: '編輯', + editArticle: '編輯作品', editComment: '編輯評論', editTag: '編輯標籤', editUserProfile: '編輯資料', @@ -342,6 +343,7 @@ export const TEXT = { draft: '草稿', DUPLICATE_TAG: '标签名称已被使用', edit: '编辑', + editArticle: '編輯作品', editComment: '编辑评论', editTag: '编辑标签', editUserProfile: '编辑资料', diff --git a/src/components/ArticleDigest/DropdownActions/EditButton.tsx b/src/components/ArticleDigest/DropdownActions/EditButton.tsx new file mode 100644 index 0000000000..37c3096fc6 --- /dev/null +++ b/src/components/ArticleDigest/DropdownActions/EditButton.tsx @@ -0,0 +1,13 @@ +import { IconEdit, Menu, TextIcon, Translate } from '~/components' + +const EditArticleButton = ({ editArticle }: { editArticle: () => void }) => { + return ( + + } size="md" spacing="base"> + + + + ) +} + +export default EditArticleButton diff --git a/src/components/ArticleDigest/DropdownActions/index.tsx b/src/components/ArticleDigest/DropdownActions/index.tsx index bdcd1bdba5..fac1e2f889 100644 --- a/src/components/ArticleDigest/DropdownActions/index.tsx +++ b/src/components/ArticleDigest/DropdownActions/index.tsx @@ -23,6 +23,7 @@ import { getQuery } from '~/common/utils' import AppreciatorsButton from './AppreciatorsButton' import ArchiveArticle from './ArchiveArticle' +import EditButton from './EditButton' import ExtendButton from './ExtendButton' import FingerprintButton from './FingerprintButton' import RemoveTagButton from './RemoveTagButton' @@ -39,6 +40,7 @@ export interface DropdownActionsControls { inUserArticles?: boolean inTagDetailLatest?: boolean inTagDetailSelected?: boolean + editArticle?: () => any } type DropdownActionsProps = { @@ -93,6 +95,7 @@ const BaseDropdownActions = ({ color = 'grey', size, inCard, + editArticle, hasAppreciators, hasFingerprint, @@ -129,6 +132,7 @@ const BaseDropdownActions = ({ {hasSetTagSelected && } {hasSetTagUnSelected && } {hasRemoveTag && } + {editArticle && } ) diff --git a/src/views/Me/DraftDetail/Sidebar/Collapsable/index.tsx b/src/components/Editor/Sidebar/Collapsable/index.tsx similarity index 100% rename from src/views/Me/DraftDetail/Sidebar/Collapsable/index.tsx rename to src/components/Editor/Sidebar/Collapsable/index.tsx diff --git a/src/views/Me/DraftDetail/Sidebar/Collapsable/styles.css b/src/components/Editor/Sidebar/Collapsable/styles.css similarity index 100% rename from src/views/Me/DraftDetail/Sidebar/Collapsable/styles.css rename to src/components/Editor/Sidebar/Collapsable/styles.css diff --git a/src/components/CollectionEditor/CollectForm.tsx b/src/components/Editor/Sidebar/Collection/CollectionEditor/CollectForm.tsx similarity index 100% rename from src/components/CollectionEditor/CollectForm.tsx rename to src/components/Editor/Sidebar/Collection/CollectionEditor/CollectForm.tsx diff --git a/src/components/CollectionEditor/index.tsx b/src/components/Editor/Sidebar/Collection/CollectionEditor/index.tsx similarity index 100% rename from src/components/CollectionEditor/index.tsx rename to src/components/Editor/Sidebar/Collection/CollectionEditor/index.tsx diff --git a/src/components/CollectionEditor/styles.css b/src/components/Editor/Sidebar/Collection/CollectionEditor/styles.css similarity index 100% rename from src/components/CollectionEditor/styles.css rename to src/components/Editor/Sidebar/Collection/CollectionEditor/styles.css diff --git a/src/components/Editor/Sidebar/Collection/index.tsx b/src/components/Editor/Sidebar/Collection/index.tsx new file mode 100644 index 0000000000..83cb50b220 --- /dev/null +++ b/src/components/Editor/Sidebar/Collection/index.tsx @@ -0,0 +1,50 @@ +import classNames from 'classnames' +import _uniq from 'lodash/uniq' +import dynamic from 'next/dynamic' + +import { Spinner, Translate } from '~/components' + +import Collapsable from '../Collapsable' +import styles from './styles.css' + +import { ArticleDigestDropdownArticle } from '~/components/ArticleDigest/Dropdown/__generated__/ArticleDigestDropdownArticle' + +const DynamicCollectionEditor = dynamic(() => import('./CollectionEditor'), { + ssr: false, + loading: Spinner, +}) + +interface CollectionProps { + articles: ArticleDigestDropdownArticle[] + onEdit: (articles: ArticleDigestDropdownArticle[]) => any + disabled?: boolean +} + +const Collection = ({ articles, onEdit, disabled }: CollectionProps) => { + const containerClasses = classNames({ + container: true, + 'u-area-disable': disabled, + }) + + return ( + } + defaultCollapsed={articles.length <= 0} + > +

+ +

+ +
+ +
+ + +
+ ) +} + +export default Collection diff --git a/src/views/Me/DraftDetail/Sidebar/CollectArticles/styles.css b/src/components/Editor/Sidebar/Collection/styles.css similarity index 100% rename from src/views/Me/DraftDetail/Sidebar/CollectArticles/styles.css rename to src/components/Editor/Sidebar/Collection/styles.css diff --git a/src/views/ArticleDetail/Collection/EditButton.tsx b/src/views/ArticleDetail/Collection/EditButton.tsx deleted file mode 100644 index ceeb20cbc9..0000000000 --- a/src/views/ArticleDetail/Collection/EditButton.tsx +++ /dev/null @@ -1,161 +0,0 @@ -import classNames from 'classnames' -import gql from 'graphql-tag' -import _uniq from 'lodash/uniq' - -import { - Button, - ButtonProps, - IconEdit, - IconPen, - IconSpinner, - TextIcon, - Translate, -} from '~/components' -import { useMutation } from '~/components/GQL' -import articleFragments from '~/components/GQL/fragments/article' - -import { ADD_TOAST } from '~/common/enums' - -import styles from './styles.css' - -import { ArticleDetailPublic_article } from '../__generated__/ArticleDetailPublic' -import { EditorSetCollection } from './__generated__/EditorSetCollection' - -/** - * Note: - * - * The response of this mutation is aligned with `COLLECTION_LIST` in `CollectionList.tsx`, - * so that it will auto update the local cache and prevent refetch logics - */ -const EDITOR_SET_COLLECTION = gql` - mutation EditorSetCollection( - $id: ID! - $after: String - $first: Int - $collection: [ID!]! - ) { - setCollection(input: { id: $id, collection: $collection }) { - ...ArticleCollection - } - } - ${articleFragments.articleCollection} -` - -const EditButton = ({ - article, - canEdit, - editing, - setEditing, - editingArticles, -}: { - article: ArticleDetailPublic_article - canEdit: boolean - editing: boolean - setEditing: any - editingArticles: string[] -}) => { - const [setCollection, { loading }] = useMutation( - EDITOR_SET_COLLECTION - ) - const onSave = async () => { - try { - await setCollection({ - variables: { - id: article.id, - collection: _uniq(editingArticles.map((item: any) => item.id)), - first: null, - }, - }) - - window.dispatchEvent( - new CustomEvent(ADD_TOAST, { - detail: { - color: 'green', - content: , - duration: 2000, - }, - }) - ) - } catch (error) { - window.dispatchEvent( - new CustomEvent(ADD_TOAST, { - detail: { - color: 'red', - content: , - clostButton: true, - duration: 2000, - }, - }) - ) - } - setEditing(false) - } - const editButtonClass = classNames({ - 'edit-button': true, - }) - - const buttonProps = { - size: ['4rem', '1.25rem'], - bgColor: 'grey-lighter', - } as ButtonProps - - if (!editing) { - return ( - - - - - - ) - } - - return ( - - - - - - - - ) -} - -export default EditButton diff --git a/src/views/ArticleDetail/Collection/EditingList.tsx b/src/views/ArticleDetail/Collection/EditingList.tsx deleted file mode 100644 index a44b2190e6..0000000000 --- a/src/views/ArticleDetail/Collection/EditingList.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import { useQuery } from '@apollo/react-hooks' -import gql from 'graphql-tag' -import _uniqBy from 'lodash/uniqBy' -import dynamic from 'next/dynamic' -import { useEffect } from 'react' - -import { Spinner } from '~/components' -import { QueryError } from '~/components/GQL' -import articleFragments from '~/components/GQL/fragments/article' - -import styles from './styles.css' - -import { ArticleDetailPublic_article } from '../__generated__/ArticleDetailPublic' -import { - EditorCollection, - EditorCollection_article_collection_edges_node, -} from './__generated__/EditorCollection' - -const EDITOR_COLLECTION = gql` - query EditorCollection($mediaHash: String) { - article(input: { mediaHash: $mediaHash }) { - ...EditorCollection - } - } - ${articleFragments.editorCollection} -` - -const CollectionEditor = dynamic( - () => import('~/components/CollectionEditor'), - { - ssr: false, - loading: Spinner, - } -) - -const EditingList = ({ - article, - editingArticles, - setEditingArticles, -}: { - article: ArticleDetailPublic_article - editingArticles: EditorCollection_article_collection_edges_node[] - setEditingArticles: ( - articles: EditorCollection_article_collection_edges_node[] - ) => void -}) => { - const { data, loading, error } = useQuery( - EDITOR_COLLECTION, - { - variables: { mediaHash: article.mediaHash }, - fetchPolicy: 'no-cache', - } - ) - const edges = data?.article?.collection.edges || [] - - // init `editingArticles` when network collection is received - const edgesKeys = edges.map(({ node }) => node.id).join(',') || '' - useEffect(() => { - setEditingArticles(edges.map(({ node }) => node)) - }, [edgesKeys]) - - if (loading) { - return - } - - if (error) { - return - } - - return ( -
- setEditingArticles(_uniqBy(articles, 'id'))} - /> - - -
- ) -} - -export default EditingList diff --git a/src/views/ArticleDetail/Collection/index.tsx b/src/views/ArticleDetail/Collection/index.tsx index 31e3818411..126cf4ce92 100644 --- a/src/views/ArticleDetail/Collection/index.tsx +++ b/src/views/ArticleDetail/Collection/index.tsx @@ -1,16 +1,13 @@ import { useQuery } from '@apollo/react-hooks' import gql from 'graphql-tag' import _uniq from 'lodash/uniq' -import { useContext, useState } from 'react' -import { Title, Translate, ViewerContext, ViewMoreButton } from '~/components' +import { Title, Translate, ViewMoreButton } from '~/components' import articleFragments from '~/components/GQL/fragments/article' import { mergeConnections } from '~/common/utils' import CollectionList from './CollectionList' -import EditButton from './EditButton' -import EditingList from './EditingList' import styles from './styles.css' import { ArticleDetailPublic_article } from '../__generated__/ArticleDetailPublic' @@ -30,11 +27,6 @@ const Collection: React.FC<{ article: ArticleDetailPublic_article collectionCount?: number }> = ({ article, collectionCount }) => { - const viewer = useContext(ViewerContext) - - const [editing, setEditing] = useState(false) - const [editingArticles, setEditingArticles] = useState([]) - const { data, loading, error, fetchMore } = useQuery( COLLECTION_LIST, { variables: { mediaHash: article.mediaHash, first: 3 } } @@ -56,9 +48,6 @@ const Collection: React.FC<{ }), }) - const isAuthor = viewer.id === article.author.id - const canEdit = isAuthor && !viewer.isInactive - return (
@@ -69,35 +58,11 @@ const Collection: React.FC<{ {collectionCount} - -
- {isAuthor && ( - - )} -
- {!editing && ( - - )} - - {editing && ( - - )} + - {!editing && pageInfo?.hasNextPage && ( - - )} + {pageInfo?.hasNextPage && }
diff --git a/src/views/ArticleDetail/Collection/styles.css b/src/views/ArticleDetail/Collection/styles.css index 52066a8798..77d283d592 100644 --- a/src/views/ArticleDetail/Collection/styles.css +++ b/src/views/ArticleDetail/Collection/styles.css @@ -1,10 +1,3 @@ -:root { - --collection-list-number-width: 1.25rem; - --collection-list-spacing: calc( - var(--collection-list-number-width) + var(--spacing-x-tight) - ); -} - .collection { margin: 0 0 var(--spacing-loose); @@ -27,21 +20,3 @@ font-weight: var(--font-weight-bold); color: var(--color-matters-green); } - -.edit-button { - line-height: 1; - - & :global(> * + *) { - margin-left: var(--spacing-x-tight); - } -} - -.editing-list { - @mixin max-height-scroll; - - padding: 0 var(--spacing-x-tight); - - @media (--lg-up) { - padding: 0 var(--spacing-loose); - } -} diff --git a/src/views/ArticleDetail/Content/index.tsx b/src/views/ArticleDetail/Content/index.tsx index 7660721ff4..9707e17a14 100644 --- a/src/views/ArticleDetail/Content/index.tsx +++ b/src/views/ArticleDetail/Content/index.tsx @@ -31,7 +31,7 @@ const Content = ({ }: { article: ContentArticle translation?: string | null - translating: boolean + translating?: boolean }) => { const [read] = useMutation(READ_ARTICLE) diff --git a/src/views/ArticleDetail/EditModeHeader/index.tsx b/src/views/ArticleDetail/EditModeHeader/index.tsx new file mode 100644 index 0000000000..93e856e50e --- /dev/null +++ b/src/views/ArticleDetail/EditModeHeader/index.tsx @@ -0,0 +1,35 @@ +import { Button, TextIcon, Translate } from '~/components' + +import styles from './styles.css' + +const EditModeHeader = ({ + setEditMode, +}: { + setEditMode: (enable: boolean) => any +}) => { + return ( + <> +

+ +

+ + + + + + ) +} + +export default EditModeHeader diff --git a/src/views/ArticleDetail/EditModeHeader/styles.css b/src/views/ArticleDetail/EditModeHeader/styles.css new file mode 100644 index 0000000000..d65cfeaaca --- /dev/null +++ b/src/views/ArticleDetail/EditModeHeader/styles.css @@ -0,0 +1,5 @@ +p { + margin-right: var(--spacing-base); + font-size: var(--font-size-sm); + color: var(--color-grey-darker); +} diff --git a/src/views/ArticleDetail/EditModeSidebar/CollectArticles.tsx b/src/views/ArticleDetail/EditModeSidebar/CollectArticles.tsx new file mode 100644 index 0000000000..e0b3b9295b --- /dev/null +++ b/src/views/ArticleDetail/EditModeSidebar/CollectArticles.tsx @@ -0,0 +1,118 @@ +import { useQuery } from '@apollo/react-hooks' +import gql from 'graphql-tag' +import _uniq from 'lodash/uniq' + +import { Spinner, Translate } from '~/components' +import SidebarCollection from '~/components/Editor/Sidebar/Collection' +import { QueryError, useMutation } from '~/components/GQL' +import articleFragments from '~/components/GQL/fragments/article' + +import { ADD_TOAST } from '~/common/enums' + +import { ArticleDigestDropdownArticle } from '~/components/ArticleDigest/Dropdown/__generated__/ArticleDigestDropdownArticle' +import { ArticleCollection } from './__generated__/ArticleCollection' +import { CollectArticlesArticle } from './__generated__/CollectArticlesArticle' +import { SetArticleCollection } from './__generated__/SetArticleCollection' + +interface CollectArticlesProps { + article: CollectArticlesArticle +} + +const fragments = { + article: gql` + fragment CollectArticlesArticle on Article { + id + } + `, +} + +const ARTICLE_COLLECTION = gql` + query ArticleCollection($id: ID!, $after: String, $first: Int = null) { + node(input: { id: $id }) { + id + ... on Article { + ...ArticleCollection + } + } + } + ${articleFragments.articleCollection} +` + +/** + * Note: + * + * The response of this mutation is aligned with `COLLECTION_LIST` in `CollectionList.tsx`, + * so that it will auto update the local cache and prevent refetch logics + */ +const SET_ARTICLE_COLLECTION = gql` + mutation SetArticleCollection( + $id: ID! + $after: String + $first: Int + $collection: [ID!]! + ) { + setCollection(input: { id: $id, collection: $collection }) { + ...ArticleCollection + } + } + ${articleFragments.articleCollection} +` + +const CollectArticles = ({ article }: CollectArticlesProps) => { + const { data, loading, error } = useQuery( + ARTICLE_COLLECTION, + { + variables: { id: article.id }, + } + ) + + const [setCollection] = useMutation( + SET_ARTICLE_COLLECTION + ) + + const handleCollectionChange = async ( + articles: ArticleDigestDropdownArticle[] + ) => { + try { + await setCollection({ + variables: { + id: article.id, + collection: _uniq(articles.map((item: any) => item.id)), + first: null, + }, + }) + } catch (e) { + window.dispatchEvent( + new CustomEvent(ADD_TOAST, { + detail: { + color: 'red', + content: , + }, + }) + ) + } + } + + if (loading) { + return + } + + if (error) { + return + } + + return ( + node)) || + [] + } + onEdit={handleCollectionChange} + /> + ) +} + +CollectArticles.fragments = fragments + +export default CollectArticles diff --git a/src/views/ArticleDetail/EditModeSidebar/index.tsx b/src/views/ArticleDetail/EditModeSidebar/index.tsx new file mode 100644 index 0000000000..82f5497738 --- /dev/null +++ b/src/views/ArticleDetail/EditModeSidebar/index.tsx @@ -0,0 +1,27 @@ +import gql from 'graphql-tag' + +import CollectArticles from './CollectArticles' + +import { EditModeSidebarArticle } from './__generated__/EditModeSidebarArticle' + +interface EditModeSidebarProps { + article: EditModeSidebarArticle +} + +const EditModeSidebar = ({ article }: EditModeSidebarProps) => ( + <> + + +) + +EditModeSidebar.fragments = { + article: gql` + fragment EditModeSidebarArticle on Article { + id + ...CollectArticlesArticle + } + ${CollectArticles.fragments.article} + `, +} + +export default EditModeSidebar diff --git a/src/views/ArticleDetail/Toolbar/index.tsx b/src/views/ArticleDetail/Toolbar/index.tsx index a501c83324..b5f77471b9 100644 --- a/src/views/ArticleDetail/Toolbar/index.tsx +++ b/src/views/ArticleDetail/Toolbar/index.tsx @@ -6,7 +6,9 @@ import { ShareButton, useResponsive, } from '~/components' -import DropdownActions from '~/components/ArticleDigest/DropdownActions' +import DropdownActions, { + DropdownActionsControls, +} from '~/components/ArticleDigest/DropdownActions' import AppreciationButton from '../AppreciationButton' import Appreciators from './Appreciators' @@ -16,9 +18,9 @@ import styles from './styles.css' import { ToolbarArticlePrivate } from './__generated__/ToolbarArticlePrivate' import { ToolbarArticlePublic } from './__generated__/ToolbarArticlePublic' -export interface ToolbarProps { +export type ToolbarProps = { article: ToolbarArticlePublic & Partial -} +} & DropdownActionsControls const fragments = { article: { @@ -49,7 +51,7 @@ const fragments = { }, } -const Toolbar = ({ article }: ToolbarProps) => { +const Toolbar = ({ article, editArticle }: ToolbarProps) => { const isSmallUp = useResponsive('sm-up') return ( @@ -73,6 +75,7 @@ const Toolbar = ({ article }: ToolbarProps) => { color="black" size="md-s" inCard={false} + editArticle={editArticle} /> diff --git a/src/views/ArticleDetail/index.tsx b/src/views/ArticleDetail/index.tsx index 94a27a42ec..969d0319a8 100644 --- a/src/views/ArticleDetail/index.tsx +++ b/src/views/ArticleDetail/index.tsx @@ -29,6 +29,8 @@ import { getQuery } from '~/common/utils' import Collection from './Collection' import Content from './Content' +import EditModeHeader from './EditModeHeader' +import EditModeSidebar from './EditModeSidebar' import FingerprintButton from './FingerprintButton' import { ARTICLE_DETAIL_PRIVATE, @@ -73,6 +75,9 @@ const ArticleDetail = () => { const [fixedWall, setFixedWall] = useState(false) // const [showResponses, setShowResponses] = useState(false) + // edit mode + const [editMode, setEditMode] = useState(false) + // wall const { data: clientPreferenceData } = useQuery( CLIENT_PREFERENCE, @@ -95,6 +100,7 @@ const ArticleDetail = () => { const authorId = article?.author?.id const collectionCount = article?.collection?.totalCount || 0 const isAuthor = viewer.id === authorId + const canEdit = isAuthor && !viewer.isInactive // fetch private data useEffect(() => { @@ -207,6 +213,28 @@ const ArticleDetail = () => { ) } + if (editMode) { + return ( + }> + } + right={} + /> +
+
+ + {translate && titleTranslation ? titleTranslation : article.title} + +
+ + +
+ + +
+ ) + } + return ( }> { }} /> */} - {(collectionCount > 0 || isAuthor) && ( + {collectionCount > 0 && (
@@ -298,7 +326,17 @@ const ArticleDetail = () => { )} - + { + setEditMode(true) + jump(document.body) + } + : undefined + } + /> {shouldShowWall && ( <> diff --git a/src/views/ArticleDetail/styles.css b/src/views/ArticleDetail/styles.css index 87d1073fae..1b1f98fe8e 100644 --- a/src/views/ArticleDetail/styles.css +++ b/src/views/ArticleDetail/styles.css @@ -1,6 +1,11 @@ .content { min-height: 100vh; padding: var(--spacing-base); + + &.editing { + cursor: not-allowed; + opacity: 0.3; + } } .title { diff --git a/src/views/Me/DraftDetail/Sidebar/AddCover/index.tsx b/src/views/Me/DraftDetail/Sidebar/AddCover/index.tsx index ebb2aed297..1e8f6ec373 100644 --- a/src/views/Me/DraftDetail/Sidebar/AddCover/index.tsx +++ b/src/views/Me/DraftDetail/Sidebar/AddCover/index.tsx @@ -3,9 +3,9 @@ import gql from 'graphql-tag' import _uniqBy from 'lodash/uniqBy' import { Translate } from '~/components' +import SidebarCollapsable from '~/components/Editor/Sidebar/Collapsable' import { useMutation } from '~/components/GQL' -import Collapsable from '../Collapsable' import styles from './styles.css' import { @@ -107,7 +107,7 @@ const AddCover = ({ draft, ...props }: AddCover) => { }) return ( - } defaultCollapsed={false} > @@ -130,7 +130,7 @@ const AddCover = ({ draft, ...props }: AddCover) => { - + ) } diff --git a/src/views/Me/DraftDetail/Sidebar/AddTags/index.tsx b/src/views/Me/DraftDetail/Sidebar/AddTags/index.tsx index d7632cb3dd..c115bc4207 100644 --- a/src/views/Me/DraftDetail/Sidebar/AddTags/index.tsx +++ b/src/views/Me/DraftDetail/Sidebar/AddTags/index.tsx @@ -3,9 +3,9 @@ import gql from 'graphql-tag' import _uniq from 'lodash/uniq' import { Translate } from '~/components' +import SidebarCollapsable from '~/components/Editor/Sidebar/Collapsable' import { useMutation } from '~/components/GQL' -import Collapsable from '../Collapsable' import SearchTags from './SearchTags' import styles from './styles.css' import Tag from './Tag' @@ -73,7 +73,10 @@ const AddTags = ({ draft, setSaveStatus }: AddTagsProps) => { } return ( - } defaultCollapsed={!hasTags}> + } + defaultCollapsed={!hasTags} + >

{ - + ) } diff --git a/src/views/Me/DraftDetail/Sidebar/CollectArticles/index.tsx b/src/views/Me/DraftDetail/Sidebar/CollectArticles/index.tsx index 19ba63b76f..78a96bc3fe 100644 --- a/src/views/Me/DraftDetail/Sidebar/CollectArticles/index.tsx +++ b/src/views/Me/DraftDetail/Sidebar/CollectArticles/index.tsx @@ -1,28 +1,16 @@ import { useQuery } from '@apollo/react-hooks' -import classNames from 'classnames' import gql from 'graphql-tag' import _uniq from 'lodash/uniq' -import dynamic from 'next/dynamic' -import { ArticleDigestDropdown, Spinner, Translate } from '~/components' +import { ArticleDigestDropdown, Spinner } from '~/components' +import SidebarCollection from '~/components/Editor/Sidebar/Collection' import { QueryError, useMutation } from '~/components/GQL' -import Collapsable from '../Collapsable' -import styles from './styles.css' - import { ArticleDigestDropdownArticle } from '~/components/ArticleDigest/Dropdown/__generated__/ArticleDigestDropdownArticle' import { CollectArticlesDraft } from './__generated__/CollectArticlesDraft' import { DraftCollectionQuery } from './__generated__/DraftCollectionQuery' import { SetDraftCollection } from './__generated__/SetDraftCollection' -const DynamicCollectionEditor = dynamic( - () => import('~/components/CollectionEditor'), - { - ssr: false, - loading: Spinner, - } -) - const fragments = { draft: gql` fragment CollectArticlesDraft on Draft { @@ -78,11 +66,8 @@ const CollectArticles = ({ draft, setSaveStatus }: CollectArticlesProps) => { const draftId = draft.id const isPending = draft.publishState === 'pending' const isPublished = draft.publishState === 'published' - const containerClasses = classNames({ - container: true, - 'u-area-disable': isPending || isPublished, - }) - const handleCollectionChange = () => async ( + + const handleCollectionChange = async ( articles: ArticleDigestDropdownArticle[] ) => { setSaveStatus('saving') @@ -113,31 +98,20 @@ const CollectArticles = ({ draft, setSaveStatus }: CollectArticlesProps) => { data.node.collection && data.node.collection.edges - return ( - } - defaultCollapsed={draft.collection.totalCount <= 0} - > -

- -

- -
- {loading && } - - {error && } + if (loading) { + return + } - node)) || []} - onEdit={handleCollectionChange()} - /> -
+ if (error) { + return + } - -
+ return ( + node)) || []} + onEdit={handleCollectionChange} + disabled={isPending || isPublished} + /> ) } From 0bfa85270d7f024df81a058ec005490ca74f9b70 Mon Sep 17 00:00:00 2001 From: RoberTu Date: Mon, 6 Jul 2020 19:58:26 +0800 Subject: [PATCH 02/86] feat(article-edit-mode): hide content for mobile --- .../Editor/Sidebar/Tags}/SearchTags.tsx | 1 + .../Editor/Sidebar/Tags}/Tag.tsx | 0 src/components/Editor/Sidebar/Tags/index.tsx | 47 ++++++++ .../Editor/Sidebar/Tags}/styles.css | 0 ...CollectArticles.tsx => EditCollection.tsx} | 14 +-- .../ArticleDetail/EditModeSidebar/index.tsx | 8 +- src/views/ArticleDetail/index.tsx | 29 +++-- .../Me/DraftDetail/Sidebar/AddTags/index.tsx | 101 ------------------ .../index.tsx => EditCollection.tsx} | 14 +-- src/views/Me/DraftDetail/Sidebar/EditTags.tsx | 76 +++++++++++++ src/views/Me/DraftDetail/Sidebar/index.tsx | 16 +-- 11 files changed, 168 insertions(+), 138 deletions(-) rename src/{views/Me/DraftDetail/Sidebar/AddTags => components/Editor/Sidebar/Tags}/SearchTags.tsx (99%) rename src/{views/Me/DraftDetail/Sidebar/AddTags => components/Editor/Sidebar/Tags}/Tag.tsx (100%) create mode 100644 src/components/Editor/Sidebar/Tags/index.tsx rename src/{views/Me/DraftDetail/Sidebar/AddTags => components/Editor/Sidebar/Tags}/styles.css (100%) rename src/views/ArticleDetail/EditModeSidebar/{CollectArticles.tsx => EditCollection.tsx} (88%) delete mode 100644 src/views/Me/DraftDetail/Sidebar/AddTags/index.tsx rename src/views/Me/DraftDetail/Sidebar/{CollectArticles/index.tsx => EditCollection.tsx} (89%) create mode 100644 src/views/Me/DraftDetail/Sidebar/EditTags.tsx diff --git a/src/views/Me/DraftDetail/Sidebar/AddTags/SearchTags.tsx b/src/components/Editor/Sidebar/Tags/SearchTags.tsx similarity index 99% rename from src/views/Me/DraftDetail/Sidebar/AddTags/SearchTags.tsx rename to src/components/Editor/Sidebar/Tags/SearchTags.tsx index a95ae5fb11..cd74a608bd 100644 --- a/src/views/Me/DraftDetail/Sidebar/AddTags/SearchTags.tsx +++ b/src/components/Editor/Sidebar/Tags/SearchTags.tsx @@ -106,6 +106,7 @@ const SearchTags = ({ addTag }: { addTag: (tag: string) => void }) => { addTag={(tag: string) => { addTag(tag) setSearch('') + close() }} /> } diff --git a/src/views/Me/DraftDetail/Sidebar/AddTags/Tag.tsx b/src/components/Editor/Sidebar/Tags/Tag.tsx similarity index 100% rename from src/views/Me/DraftDetail/Sidebar/AddTags/Tag.tsx rename to src/components/Editor/Sidebar/Tags/Tag.tsx diff --git a/src/components/Editor/Sidebar/Tags/index.tsx b/src/components/Editor/Sidebar/Tags/index.tsx new file mode 100644 index 0000000000..5741f61c21 --- /dev/null +++ b/src/components/Editor/Sidebar/Tags/index.tsx @@ -0,0 +1,47 @@ +import classNames from 'classnames' +import _uniq from 'lodash/uniq' + +import { Translate } from '~/components' + +import Collapsable from '../Collapsable' +import SearchTags from './SearchTags' +import styles from './styles.css' +import Tag from './Tag' + +interface AddTagsProps { + tags: string[] + onAddTag: (tag: string) => void + onDeleteTag: (tag: string) => void + disabled?: boolean +} + +const AddTags = ({ tags, onAddTag, onDeleteTag, disabled }: AddTagsProps) => { + const hasTags = tags.length > 0 + const tagsContainerClasses = classNames({ + 'tags-container': true, + 'u-area-disable': disabled, + }) + + return ( + } defaultCollapsed={!hasTags}> +

+ +

+ +
+ {tags.map((tag) => ( + + ))} + + +
+ + +
+ ) +} + +export default AddTags diff --git a/src/views/Me/DraftDetail/Sidebar/AddTags/styles.css b/src/components/Editor/Sidebar/Tags/styles.css similarity index 100% rename from src/views/Me/DraftDetail/Sidebar/AddTags/styles.css rename to src/components/Editor/Sidebar/Tags/styles.css diff --git a/src/views/ArticleDetail/EditModeSidebar/CollectArticles.tsx b/src/views/ArticleDetail/EditModeSidebar/EditCollection.tsx similarity index 88% rename from src/views/ArticleDetail/EditModeSidebar/CollectArticles.tsx rename to src/views/ArticleDetail/EditModeSidebar/EditCollection.tsx index e0b3b9295b..7b9e1bef9d 100644 --- a/src/views/ArticleDetail/EditModeSidebar/CollectArticles.tsx +++ b/src/views/ArticleDetail/EditModeSidebar/EditCollection.tsx @@ -11,16 +11,16 @@ import { ADD_TOAST } from '~/common/enums' import { ArticleDigestDropdownArticle } from '~/components/ArticleDigest/Dropdown/__generated__/ArticleDigestDropdownArticle' import { ArticleCollection } from './__generated__/ArticleCollection' -import { CollectArticlesArticle } from './__generated__/CollectArticlesArticle' +import { EditCollectionArticle } from './__generated__/EditCollectionArticle' import { SetArticleCollection } from './__generated__/SetArticleCollection' -interface CollectArticlesProps { - article: CollectArticlesArticle +interface EditCollectionProps { + article: EditCollectionArticle } const fragments = { article: gql` - fragment CollectArticlesArticle on Article { + fragment EditCollectionArticle on Article { id } `, @@ -58,7 +58,7 @@ const SET_ARTICLE_COLLECTION = gql` ${articleFragments.articleCollection} ` -const CollectArticles = ({ article }: CollectArticlesProps) => { +const EditCollection = ({ article }: EditCollectionProps) => { const { data, loading, error } = useQuery( ARTICLE_COLLECTION, { @@ -113,6 +113,6 @@ const CollectArticles = ({ article }: CollectArticlesProps) => { ) } -CollectArticles.fragments = fragments +EditCollection.fragments = fragments -export default CollectArticles +export default EditCollection diff --git a/src/views/ArticleDetail/EditModeSidebar/index.tsx b/src/views/ArticleDetail/EditModeSidebar/index.tsx index 82f5497738..3fdd41f4ac 100644 --- a/src/views/ArticleDetail/EditModeSidebar/index.tsx +++ b/src/views/ArticleDetail/EditModeSidebar/index.tsx @@ -1,6 +1,6 @@ import gql from 'graphql-tag' -import CollectArticles from './CollectArticles' +import EditCollection from './EditCollection' import { EditModeSidebarArticle } from './__generated__/EditModeSidebarArticle' @@ -10,7 +10,7 @@ interface EditModeSidebarProps { const EditModeSidebar = ({ article }: EditModeSidebarProps) => ( <> - + ) @@ -18,9 +18,9 @@ EditModeSidebar.fragments = { article: gql` fragment EditModeSidebarArticle on Article { id - ...CollectArticlesArticle + ...EditCollectionArticle } - ${CollectArticles.fragments.article} + ${EditCollection.fragments.article} `, } diff --git a/src/views/ArticleDetail/index.tsx b/src/views/ArticleDetail/index.tsx index 969d0319a8..ef9b2742b7 100644 --- a/src/views/ArticleDetail/index.tsx +++ b/src/views/ArticleDetail/index.tsx @@ -216,19 +216,26 @@ const ArticleDetail = () => { if (editMode) { return ( }> - } - right={} - /> -
-
- - {translate && titleTranslation ? titleTranslation : article.title} - + } /> + {isLargeUp && ( +
+
+ + {translate && titleTranslation + ? titleTranslation + : article.title} + +
+ +
+ )} - -
+ {!isLargeUp && ( + + + + )} diff --git a/src/views/Me/DraftDetail/Sidebar/AddTags/index.tsx b/src/views/Me/DraftDetail/Sidebar/AddTags/index.tsx deleted file mode 100644 index c115bc4207..0000000000 --- a/src/views/Me/DraftDetail/Sidebar/AddTags/index.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import classNames from 'classnames' -import gql from 'graphql-tag' -import _uniq from 'lodash/uniq' - -import { Translate } from '~/components' -import SidebarCollapsable from '~/components/Editor/Sidebar/Collapsable' -import { useMutation } from '~/components/GQL' - -import SearchTags from './SearchTags' -import styles from './styles.css' -import Tag from './Tag' - -import { AddTagsDraft } from './__generated__/AddTagsDraft' -import { UpdateDraftTags } from './__generated__/UpdateDraftTags' - -const fragments = { - draft: gql` - fragment AddTagsDraft on Draft { - id - tags - publishState - } - `, -} - -const UPDATE_TAGS = gql` - mutation UpdateDraftTags($id: ID!, $tags: [String]!) { - putDraft(input: { id: $id, tags: $tags }) { - id - ...AddTagsDraft - } - } - ${fragments.draft} -` - -interface AddTagsProps { - draft: AddTagsDraft - setSaveStatus: (status: 'saved' | 'saving' | 'saveFailed') => void -} - -const AddTags = ({ draft, setSaveStatus }: AddTagsProps) => { - const [updateTags] = useMutation(UPDATE_TAGS) - // const draftId = draft.id - const tags = draft.tags || [] - const hasTags = tags.length > 0 - const isPending = draft.publishState === 'pending' - const isPublished = draft.publishState === 'published' - const tagsContainerClasses = classNames({ - 'tags-container': true, - 'u-area-disable': isPending || isPublished, - }) - const addTag = async (tag: string) => { - setSaveStatus('saving') - try { - await updateTags({ - variables: { id: draft.id, tags: _uniq(tags.concat(tag)) }, - }) - setSaveStatus('saved') - } catch (e) { - setSaveStatus('saveFailed') - } - } - const deleteTag = async (tag: string) => { - setSaveStatus('saving') - try { - await updateTags({ - variables: { id: draft.id, tags: tags.filter((it) => it !== tag) }, - }) - setSaveStatus('saved') - } catch (e) { - setSaveStatus('saveFailed') - } - } - - return ( - } - defaultCollapsed={!hasTags} - > -

- -

- -
- {tags.map((tag) => ( - - ))} - -
- - -
- ) -} - -AddTags.fragments = fragments - -export default AddTags diff --git a/src/views/Me/DraftDetail/Sidebar/CollectArticles/index.tsx b/src/views/Me/DraftDetail/Sidebar/EditCollection.tsx similarity index 89% rename from src/views/Me/DraftDetail/Sidebar/CollectArticles/index.tsx rename to src/views/Me/DraftDetail/Sidebar/EditCollection.tsx index 78a96bc3fe..825b941bd1 100644 --- a/src/views/Me/DraftDetail/Sidebar/CollectArticles/index.tsx +++ b/src/views/Me/DraftDetail/Sidebar/EditCollection.tsx @@ -7,13 +7,13 @@ import SidebarCollection from '~/components/Editor/Sidebar/Collection' import { QueryError, useMutation } from '~/components/GQL' import { ArticleDigestDropdownArticle } from '~/components/ArticleDigest/Dropdown/__generated__/ArticleDigestDropdownArticle' -import { CollectArticlesDraft } from './__generated__/CollectArticlesDraft' import { DraftCollectionQuery } from './__generated__/DraftCollectionQuery' +import { EditCollectionDraft } from './__generated__/EditCollectionDraft' import { SetDraftCollection } from './__generated__/SetDraftCollection' const fragments = { draft: gql` - fragment CollectArticlesDraft on Draft { + fragment EditCollectionDraft on Draft { id publishState collection(input: { first: 0 }) { @@ -57,12 +57,12 @@ const SET_DRAFT_COLLECTION = gql` ${ArticleDigestDropdown.fragments.article} ` -interface CollectArticlesProps { - draft: CollectArticlesDraft +interface EditCollectionProps { + draft: EditCollectionDraft setSaveStatus: (status: 'saved' | 'saving' | 'saveFailed') => void } -const CollectArticles = ({ draft, setSaveStatus }: CollectArticlesProps) => { +const EditCollection = ({ draft, setSaveStatus }: EditCollectionProps) => { const draftId = draft.id const isPending = draft.publishState === 'pending' const isPublished = draft.publishState === 'published' @@ -115,6 +115,6 @@ const CollectArticles = ({ draft, setSaveStatus }: CollectArticlesProps) => { ) } -CollectArticles.fragments = fragments +EditCollection.fragments = fragments -export default CollectArticles +export default EditCollection diff --git a/src/views/Me/DraftDetail/Sidebar/EditTags.tsx b/src/views/Me/DraftDetail/Sidebar/EditTags.tsx new file mode 100644 index 0000000000..c12ea6bc62 --- /dev/null +++ b/src/views/Me/DraftDetail/Sidebar/EditTags.tsx @@ -0,0 +1,76 @@ +import gql from 'graphql-tag' +import _uniq from 'lodash/uniq' + +import SidebarTags from '~/components/Editor/Sidebar/Tags' +import { useMutation } from '~/components/GQL' + +import { EditTagsDraft } from './__generated__/EditTagsDraft' +import { UpdateDraftTags } from './__generated__/UpdateDraftTags' + +const fragments = { + draft: gql` + fragment EditTagsDraft on Draft { + id + tags + publishState + } + `, +} + +const UPDATE_TAGS = gql` + mutation UpdateDraftTags($id: ID!, $tags: [String]!) { + putDraft(input: { id: $id, tags: $tags }) { + id + ...EditTagsDraft + } + } + ${fragments.draft} +` + +interface EditTagsProps { + draft: EditTagsDraft + setSaveStatus: (status: 'saved' | 'saving' | 'saveFailed') => void +} + +const EditTags = ({ draft, setSaveStatus }: EditTagsProps) => { + const [updateTags] = useMutation(UPDATE_TAGS) + const tags = draft.tags || [] + const isPending = draft.publishState === 'pending' + const isPublished = draft.publishState === 'published' + + const addTag = async (tag: string) => { + setSaveStatus('saving') + try { + await updateTags({ + variables: { id: draft.id, tags: _uniq(tags.concat(tag)) }, + }) + setSaveStatus('saved') + } catch (e) { + setSaveStatus('saveFailed') + } + } + const deleteTag = async (tag: string) => { + setSaveStatus('saving') + try { + await updateTags({ + variables: { id: draft.id, tags: tags.filter((it) => it !== tag) }, + }) + setSaveStatus('saved') + } catch (e) { + setSaveStatus('saveFailed') + } + } + + return ( + + ) +} + +EditTags.fragments = fragments + +export default EditTags diff --git a/src/views/Me/DraftDetail/Sidebar/index.tsx b/src/views/Me/DraftDetail/Sidebar/index.tsx index da8370f804..f751e92239 100644 --- a/src/views/Me/DraftDetail/Sidebar/index.tsx +++ b/src/views/Me/DraftDetail/Sidebar/index.tsx @@ -1,8 +1,8 @@ import gql from 'graphql-tag' import AddCover from './AddCover' -import AddTags from './AddTags' -import CollectArticles from './CollectArticles' +import EditCollection from './EditCollection' +import EditTags from './EditTags' import { DraftSidebarDraft } from './__generated__/DraftSidebarDraft' @@ -14,8 +14,8 @@ interface SidebarProps { const Sidebar = ({ draft, setSaveStatus }: SidebarProps) => ( <> - - + + ) @@ -24,12 +24,12 @@ Sidebar.fragments = { fragment DraftSidebarDraft on Draft { id ...AddCoverDraft - ...AddTagsDraft - ...CollectArticlesDraft + ...EditTagsDraft + ...EditCollectionDraft } ${AddCover.fragments.draft} - ${AddTags.fragments.draft} - ${CollectArticles.fragments.draft} + ${EditTags.fragments.draft} + ${EditCollection.fragments.draft} `, } From 5fd7a85fd8540cf2fe68a7c0fd54ea6539643bc2 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 7 Jul 2020 02:18:55 +0000 Subject: [PATCH 03/86] build(deps-dev): bump chromedriver from 83.0.0 to 83.0.1 Bumps [chromedriver](https://github.com/giggio/node-chromedriver) from 83.0.0 to 83.0.1. - [Release notes](https://github.com/giggio/node-chromedriver/releases) - [Commits](https://github.com/giggio/node-chromedriver/compare/83.0.0...83.0.1) Signed-off-by: dependabot-preview[bot] --- package-lock.json | 57 +++++++++++++++++++++++++++++++---------------- package.json | 2 +- 2 files changed, 39 insertions(+), 20 deletions(-) diff --git a/package-lock.json b/package-lock.json index c2be8965e4..a7c7da0421 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5791,12 +5791,6 @@ "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", "dev": true }, - "@types/events": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", - "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==", - "dev": true - }, "@types/express": { "version": "4.17.6", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.6.tgz", @@ -5827,12 +5821,11 @@ "dev": true }, "@types/glob": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", - "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==", "dev": true, "requires": { - "@types/events": "*", "@types/minimatch": "*", "@types/node": "*" } @@ -8767,24 +8760,50 @@ } }, "chromedriver": { - "version": "83.0.0", - "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-83.0.0.tgz", - "integrity": "sha512-AePp9ykma+z4aKPRqlbzvVlc22VsQ6+rgF+0aL3B5onHOncK18dWSkLrSSJMczP/mXILN9ohGsvpuTwoRSj6OQ==", + "version": "83.0.1", + "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-83.0.1.tgz", + "integrity": "sha512-51/YsLIMRF+L0ooMlM4aZjyoOpDs0gDXGlT6+/CwWEnvK53PUyef9FkotKbzknCaUeL/qUw3ic3IMmsNc+SUxg==", "dev": true, "requires": { "@testim/chrome-version": "^1.0.7", "axios": "^0.19.2", "del": "^5.1.0", "extract-zip": "^2.0.0", + "https-proxy-agent": "^2.2.4", "mkdirp": "^1.0.4", "tcp-port-used": "^1.0.1" }, "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "https-proxy-agent": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", + "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", + "dev": true, + "requires": { + "agent-base": "^4.3.0", + "debug": "^3.1.0" + } + }, "mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true } } }, @@ -11813,9 +11832,9 @@ "dev": true }, "extract-zip": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.0.tgz", - "integrity": "sha512-i42GQ498yibjdvIhivUsRslx608whtGoFIhF26Z7O4MYncBxp8CwalOs1lnHy21A9sIohWO2+uiE4SRtC9JXDg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", "dev": true, "requires": { "@types/yauzl": "^2.9.1", @@ -13549,9 +13568,9 @@ "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=" }, "ignore": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.6.tgz", - "integrity": "sha512-cgXgkypZBcCnOgSihyeqbo6gjIaIyDqPQB7Ra4vhE9m6kigdGoQDMHjviFhRZo3IMlRy6yElosoviMs5YxZXUA==", + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", "dev": true }, "imagemin": { diff --git a/package.json b/package.json index 10408b8550..1ab3e61f6d 100644 --- a/package.json +++ b/package.json @@ -115,7 +115,7 @@ "babel-jest": "^26.1.0", "babel-plugin-dynamic-import-node": "^2.3.3", "babel-polyfill": "^6.26.0", - "chromedriver": "^83.0.0", + "chromedriver": "^83.0.1", "cucumber": "^6.0.5", "cucumber-pretty": "^6.0.0", "cz-conventional-changelog": "^3.2.0", From c939f0fb31f264da926b674dd1532d07d0c75ca0 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 7 Jul 2020 02:21:23 +0000 Subject: [PATCH 04/86] build(deps-dev): bump @types/jest from 26.0.3 to 26.0.4 Bumps [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest) from 26.0.3 to 26.0.4. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest) Signed-off-by: dependabot-preview[bot] --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index c2be8965e4..8e5464d822 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5887,9 +5887,9 @@ } }, "@types/jest": { - "version": "26.0.3", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.3.tgz", - "integrity": "sha512-v89ga1clpVL/Y1+YI0eIu1VMW+KU7Xl8PhylVtDKVWaSUHBHYPLXMQGBdrpHewaKoTvlXkksbYqPgz8b4cmRZg==", + "version": "26.0.4", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.4.tgz", + "integrity": "sha512-4fQNItvelbNA9+sFgU+fhJo8ZFF+AS4Egk3GWwCW2jFtViukXbnztccafAdLhzE/0EiCogljtQQXP8aQ9J7sFg==", "dev": true, "requires": { "jest-diff": "^25.2.1", diff --git a/package.json b/package.json index 10408b8550..6805c049d2 100644 --- a/package.json +++ b/package.json @@ -96,7 +96,7 @@ "@types/fingerprintjs2": "^2.0.0", "@types/grecaptcha": "^3.0.1", "@types/helmet": "0.0.47", - "@types/jest": "^26.0.3", + "@types/jest": "^26.0.4", "@types/jump.js": "^1.0.3", "@types/lodash": "^4.14.157", "@types/nprogress": "0.2.0", From 73aee638e006ef30d4f5d7a3b457d720288c05ba Mon Sep 17 00:00:00 2001 From: Zeck Li Date: Tue, 7 Jul 2020 17:02:15 +0800 Subject: [PATCH 05/86] feat(component): change shared components for Tag detail page refactoring --- .../DropdownActions/RemoveTagButton.tsx | 2 +- .../DropdownActions/SetTagSelectedButton.tsx | 6 +- src/components/Button/index.tsx | 4 +- src/components/Button/styles.css | 14 ++++ .../GQL/queries/tagArticlesCount.ts | 14 ++++ src/components/GQL/queries/tagFollowers.ts | 26 +++++++ src/components/GQL/updates/tagFollowers.ts | 71 +++++++++++++++++++ src/components/Icon/IconAddMedium.tsx | 4 ++ src/components/Icon/index.tsx | 1 + src/components/Title/index.tsx | 2 +- src/components/Title/styles.css | 3 +- 11 files changed, 139 insertions(+), 8 deletions(-) create mode 100644 src/components/GQL/queries/tagArticlesCount.ts create mode 100644 src/components/GQL/queries/tagFollowers.ts create mode 100644 src/components/GQL/updates/tagFollowers.ts create mode 100644 src/components/Icon/IconAddMedium.tsx diff --git a/src/components/ArticleDigest/DropdownActions/RemoveTagButton.tsx b/src/components/ArticleDigest/DropdownActions/RemoveTagButton.tsx index cb5d431420..a98e5d4203 100644 --- a/src/components/ArticleDigest/DropdownActions/RemoveTagButton.tsx +++ b/src/components/ArticleDigest/DropdownActions/RemoveTagButton.tsx @@ -65,7 +65,7 @@ const RemoveTagButton = ({ article }: { article: RemoveTagButtonArticle }) => { }} > } size="md" spacing="base"> - + ) diff --git a/src/components/ArticleDigest/DropdownActions/SetTagSelectedButton.tsx b/src/components/ArticleDigest/DropdownActions/SetTagSelectedButton.tsx index 2f4efc57a2..6323c1fb43 100644 --- a/src/components/ArticleDigest/DropdownActions/SetTagSelectedButton.tsx +++ b/src/components/ArticleDigest/DropdownActions/SetTagSelectedButton.tsx @@ -1,7 +1,7 @@ import gql from 'graphql-tag' import { useRouter } from 'next/router' -import { IconPinMedium, Menu, TextIcon, Translate } from '~/components' +import { IconAddMedium, Menu, TextIcon, Translate } from '~/components' import { useMutation } from '~/components/GQL' import { ADD_TOAST } from '~/common/enums' @@ -70,8 +70,8 @@ const SetTagSelectedButton = ({ ) }} > - } size="md" spacing="base"> - + } size="md" spacing="base"> + ) diff --git a/src/components/Button/index.tsx b/src/components/Button/index.tsx index 1c1e59fd57..62fa55dab2 100644 --- a/src/components/Button/index.tsx +++ b/src/components/Button/index.tsx @@ -54,7 +54,7 @@ type ButtonColor = | 'gold' | 'red' -type ButtonTextColor = Extract +type ButtonTextColor = Extract export type ButtonBgColor = Extract< ButtonColor, @@ -70,7 +70,7 @@ export type ButtonBgColor = Extract< type ButtonBgActiveColor = Extract< ButtonColor, - 'grey-lighter' | 'green-lighter' | 'grey-lighter-active' | 'green' | 'red' + 'grey-lighter' | 'green-lighter' | 'grey-lighter-active' | 'green' | 'gold' | 'red' > export interface ButtonProps { diff --git a/src/components/Button/styles.css b/src/components/Button/styles.css index 72d38d7d41..7b64d85060 100644 --- a/src/components/Button/styles.css +++ b/src/components/Button/styles.css @@ -21,6 +21,10 @@ span.container { color: var(--color-matters-green); } +.text-gold { + color: var(--color-matters-gold); +} + .text-red { color: var(--color-red); } @@ -317,6 +321,16 @@ span.container { } } +.bg-active-gold { + &:hover, + &:focus { + & .hotarea { + color: var(--color-white); + background: var(--color-matters-gold); + } + } +} + .bg-active-red { &:hover, &:focus { diff --git a/src/components/GQL/queries/tagArticlesCount.ts b/src/components/GQL/queries/tagArticlesCount.ts new file mode 100644 index 0000000000..04e476b03d --- /dev/null +++ b/src/components/GQL/queries/tagArticlesCount.ts @@ -0,0 +1,14 @@ +import gql from 'graphql-tag' + +export default gql` + query TagArticlesCount($id: ID!) { + node(input: { id: $id }) { + ... on Tag { + id + articles(input: { first: 0, selected: false }) { + totalCount + } + } + } + } +` diff --git a/src/components/GQL/queries/tagFollowers.ts b/src/components/GQL/queries/tagFollowers.ts new file mode 100644 index 0000000000..99bab6db26 --- /dev/null +++ b/src/components/GQL/queries/tagFollowers.ts @@ -0,0 +1,26 @@ +import gql from 'graphql-tag' + +import { Avatar } from '~/components' + +export default gql` + query TagFollowers($id: ID!) { + node(input: { id: $id }) { + ... on Tag { + id + followers(input: { first: 5 }) { + totalCount + edges { + cursor + node { + ... on User { + id + ...AvatarUser + } + } + } + } + } + } + } + ${Avatar.fragments.user} +` diff --git a/src/components/GQL/updates/tagFollowers.ts b/src/components/GQL/updates/tagFollowers.ts new file mode 100644 index 0000000000..63ac7365a6 --- /dev/null +++ b/src/components/GQL/updates/tagFollowers.ts @@ -0,0 +1,71 @@ +import { DataProxy } from 'apollo-cache' + +import TAG_FOLLOWERS from '~/components/GQL/queries/tagFollowers' + +import { ERROR_CODES } from '~/common/enums' + +import { TagFollowers } from '~/components/GQL/queries/__generated__/TagFollowers' + +const update = ({ + cache, + id, + type, + viewer, +}: { + cache: DataProxy + id: string + type: 'follow' | 'unfollow' + viewer: any +}) => { + try { + if (!id) { + return + } + + const variables = { id } + const cacheData = cache.readQuery({ + query: TAG_FOLLOWERS, + variables, + }) + + if (!cacheData || !cacheData.node || cacheData.node.__typename !== 'Tag') { + return + } + + const followers = cacheData.node.followers.edges || [] + if (type === 'follow') { + followers.push({ + cursor: window.btoa(`arrayconnection:${followers.length}:0`) || '', + node: { + avatar: viewer.avatar, + id: viewer.id, + liker: { + civicLiker: viewer.liker.civicLiker, + __typename: 'Liker', + }, + __typename: 'User', + }, + __typename: 'UserEdge', + }) + cacheData.node.followers.edges = followers + cacheData.node.followers.totalCount++ + } else { + cacheData.node.followers.edges = followers.filter(follower => follower.node.id !== viewer.id) + cacheData.node.followers.totalCount-- + } + + cache.writeQuery({ + query: TAG_FOLLOWERS, + variables, + data: cacheData, + }) + } catch (e) { + if (e.message.startsWith("Can't find field")) { + console.warn(ERROR_CODES.QUERY_FIELD_NOT_FOUND) + } else { + console.error(e) + } + } +} + +export default update diff --git a/src/components/Icon/IconAddMedium.tsx b/src/components/Icon/IconAddMedium.tsx new file mode 100644 index 0000000000..33123addd9 --- /dev/null +++ b/src/components/Icon/IconAddMedium.tsx @@ -0,0 +1,4 @@ +import { ReactComponent as AddMedium } from './icons/add-md.svg' +import { withIcon } from './withIcon' + +export const IconAddMedium = withIcon(AddMedium) diff --git a/src/components/Icon/index.tsx b/src/components/Icon/index.tsx index 708a81925d..4d8e736343 100644 --- a/src/components/Icon/index.tsx +++ b/src/components/Icon/index.tsx @@ -20,6 +20,7 @@ export * from './withIcon' export * from './IconAdd' +export * from './IconAddMedium' export * from './IconAppreciationMAX' export * from './IconArchiveMedium' export * from './IconAvatarLogo' diff --git a/src/components/Title/index.tsx b/src/components/Title/index.tsx index 28db018049..273bc61568 100644 --- a/src/components/Title/index.tsx +++ b/src/components/Title/index.tsx @@ -3,7 +3,7 @@ import React from 'react' import styles from './styles.css' -type TitleType = 'article' | 'feed' | 'sidebar' | 'nav' +type TitleType = 'article' | 'feed' | 'sidebar' | 'nav' | 'tag' type TitleIs = 'h1' | 'h2' | 'h3' diff --git a/src/components/Title/styles.css b/src/components/Title/styles.css index 725be38755..d2f74c2d61 100644 --- a/src/components/Title/styles.css +++ b/src/components/Title/styles.css @@ -1,4 +1,5 @@ -.article { +.article, +.tag { font-size: var(--font-size-article-title); font-weight: var(--font-weight-article-title); line-height: var(--line-height-article-title); From f3e6a10ca58a5ae348bd27564b003b3dace8435a Mon Sep 17 00:00:00 2001 From: RoberTu Date: Tue, 7 Jul 2020 19:21:51 +0800 Subject: [PATCH 06/86] feat(edit-article): use `editArticle` mutation instaed of `setCollection` --- .../ArticleDigest/DropdownActions/index.tsx | 6 +- .../ArticleDetail/EditMode/Header/index.tsx | 104 +++++++++++++++ .../Header}/styles.css | 0 .../EditMode/Sidebar/EditCollection.tsx | 63 ++++++++++ .../ArticleDetail/EditMode/Sidebar/index.tsx | 14 +++ src/views/ArticleDetail/EditMode/index.tsx | 7 ++ .../ArticleDetail/EditModeHeader/index.tsx | 35 ------ .../EditModeSidebar/EditCollection.tsx | 118 ------------------ .../ArticleDetail/EditModeSidebar/index.tsx | 27 ---- src/views/ArticleDetail/index.tsx | 36 +++++- 10 files changed, 224 insertions(+), 186 deletions(-) create mode 100644 src/views/ArticleDetail/EditMode/Header/index.tsx rename src/views/ArticleDetail/{EditModeHeader => EditMode/Header}/styles.css (100%) create mode 100644 src/views/ArticleDetail/EditMode/Sidebar/EditCollection.tsx create mode 100644 src/views/ArticleDetail/EditMode/Sidebar/index.tsx create mode 100644 src/views/ArticleDetail/EditMode/index.tsx delete mode 100644 src/views/ArticleDetail/EditModeHeader/index.tsx delete mode 100644 src/views/ArticleDetail/EditModeSidebar/EditCollection.tsx delete mode 100644 src/views/ArticleDetail/EditModeSidebar/index.tsx diff --git a/src/components/ArticleDigest/DropdownActions/index.tsx b/src/components/ArticleDigest/DropdownActions/index.tsx index fac1e2f889..c3cd9ed49f 100644 --- a/src/components/ArticleDigest/DropdownActions/index.tsx +++ b/src/components/ArticleDigest/DropdownActions/index.tsx @@ -56,6 +56,7 @@ interface Controls { hasSetTagSelected: boolean hasSetTagUnSelected: boolean hasRemoveTag: boolean + hasEdit: boolean } interface DialogProps { @@ -105,6 +106,7 @@ const BaseDropdownActions = ({ hasSetTagSelected, hasSetTagUnSelected, hasRemoveTag, + hasEdit, openFingerprintDialog, openAppreciatorsDialog, @@ -132,7 +134,7 @@ const BaseDropdownActions = ({ {hasSetTagSelected && } {hasSetTagUnSelected && } {hasRemoveTag && } - {editArticle && } + {hasEdit && editArticle && } ) @@ -169,6 +171,7 @@ const DropdownActions = (props: DropdownActionsProps) => { inUserArticles, inTagDetailLatest, inTagDetailSelected, + editArticle, } = props const router = useRouter() const viewer = useContext(ViewerContext) @@ -205,6 +208,7 @@ const DropdownActions = (props: DropdownActionsProps) => { hasSetTagSelected: !!(inTagDetailLatest && canEditTag), hasSetTagUnSelected: !!(inTagDetailSelected && canEditTag), hasRemoveTag: !!(isInTagDetail && canEditTag), + hasEdit: isActive && !!editArticle, } if (_isEmpty(_pickBy(controls))) { diff --git a/src/views/ArticleDetail/EditMode/Header/index.tsx b/src/views/ArticleDetail/EditMode/Header/index.tsx new file mode 100644 index 0000000000..7bd646bd9b --- /dev/null +++ b/src/views/ArticleDetail/EditMode/Header/index.tsx @@ -0,0 +1,104 @@ +import gql from 'graphql-tag' +import _uniq from 'lodash/uniq' + +import { Button, IconSpinner, TextIcon, Translate } from '~/components' +import { useMutation } from '~/components/GQL' +import articleFragments from '~/components/GQL/fragments/article' + +import { ADD_TOAST } from '~/common/enums' + +import styles from './styles.css' + +import { ArticleDigestDropdownArticle } from '~/components/ArticleDigest/Dropdown/__generated__/ArticleDigestDropdownArticle' +import { EditArticle } from './__generated__/EditArticle' + +interface EditModeHeaderProps { + id: string + tags: string[] + collection: ArticleDigestDropdownArticle[] + setEditMode: (enable: boolean) => any +} + +/** + * Note: + * + * The response of this mutation is aligned with `COLLECTION_LIST` in `CollectionList.tsx`, + * so that it will auto update the local cache and prevent refetch logics + */ + +const EDIT_ARTICLE = gql` + mutation EditArticle( + $id: ID! + $collection: [ID!] + $after: String + $first: Int = null + ) { + editArticle(input: { id: $id, collection: $collection }) { + id + ...ArticleCollection + } + } + ${articleFragments.articleCollection} +` + +const EditModeHeader = ({ + id, + tags, + collection, + setEditMode, +}: EditModeHeaderProps) => { + const [editArticle, { loading }] = useMutation(EDIT_ARTICLE) + + const onSave = async () => { + try { + await editArticle({ + variables: { + id, + collection: _uniq(collection.map(({ id: articleId }) => articleId)), + first: null, + }, + }) + setEditMode(false) + } catch (e) { + window.dispatchEvent( + new CustomEvent(ADD_TOAST, { + detail: { + color: 'red', + content: , + }, + }) + ) + } + } + + return ( + <> +

+ +

+ + + + + + ) +} + +export default EditModeHeader diff --git a/src/views/ArticleDetail/EditModeHeader/styles.css b/src/views/ArticleDetail/EditMode/Header/styles.css similarity index 100% rename from src/views/ArticleDetail/EditModeHeader/styles.css rename to src/views/ArticleDetail/EditMode/Header/styles.css diff --git a/src/views/ArticleDetail/EditMode/Sidebar/EditCollection.tsx b/src/views/ArticleDetail/EditMode/Sidebar/EditCollection.tsx new file mode 100644 index 0000000000..67c02683ff --- /dev/null +++ b/src/views/ArticleDetail/EditMode/Sidebar/EditCollection.tsx @@ -0,0 +1,63 @@ +import { useQuery } from '@apollo/react-hooks' +import gql from 'graphql-tag' +import _uniq from 'lodash/uniq' +import { useEffect } from 'react' + +import { Spinner } from '~/components' +import SidebarCollection from '~/components/Editor/Sidebar/Collection' +import { QueryError } from '~/components/GQL' +import articleFragments from '~/components/GQL/fragments/article' + +import { ArticleDigestDropdownArticle } from '~/components/ArticleDigest/Dropdown/__generated__/ArticleDigestDropdownArticle' +import { ArticleCollection } from './__generated__/ArticleCollection' + +export interface EditCollectionProps { + articleId: string + collection: ArticleDigestDropdownArticle[] + setCollection: (articles: ArticleDigestDropdownArticle[]) => any +} + +const ARTICLE_COLLECTION = gql` + query ArticleCollection($id: ID!, $after: String, $first: Int = null) { + node(input: { id: $id }) { + id + ... on Article { + ...ArticleCollection + } + } + } + ${articleFragments.articleCollection} +` + +const EditCollection = ({ + articleId, + collection, + setCollection, +}: EditCollectionProps) => { + const { data, loading, error } = useQuery( + ARTICLE_COLLECTION, + { + variables: { id: articleId }, + } + ) + + useEffect(() => { + const articles = + (data?.node?.__typename === 'Article' && + data.node.collection.edges?.map(({ node }) => node)) || + [] + setCollection(articles) + }, [data?.node?.__typename]) + + if (loading) { + return + } + + if (error) { + return + } + + return +} + +export default EditCollection diff --git a/src/views/ArticleDetail/EditMode/Sidebar/index.tsx b/src/views/ArticleDetail/EditMode/Sidebar/index.tsx new file mode 100644 index 0000000000..fd44d1f4f8 --- /dev/null +++ b/src/views/ArticleDetail/EditMode/Sidebar/index.tsx @@ -0,0 +1,14 @@ +import EditCollection, { EditCollectionProps } from './EditCollection' + +type EditModeSidebarProps = { + tags: string[] + setTags: (tags: string[]) => any +} & EditCollectionProps + +const EditModeSidebar = (props: EditModeSidebarProps) => ( + <> + + +) + +export default EditModeSidebar diff --git a/src/views/ArticleDetail/EditMode/index.tsx b/src/views/ArticleDetail/EditMode/index.tsx new file mode 100644 index 0000000000..7d2f502289 --- /dev/null +++ b/src/views/ArticleDetail/EditMode/index.tsx @@ -0,0 +1,7 @@ +import Header from './Header' +import Sidebar from './Sidebar' + +export default { + Header, + Sidebar, +} diff --git a/src/views/ArticleDetail/EditModeHeader/index.tsx b/src/views/ArticleDetail/EditModeHeader/index.tsx deleted file mode 100644 index 93e856e50e..0000000000 --- a/src/views/ArticleDetail/EditModeHeader/index.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { Button, TextIcon, Translate } from '~/components' - -import styles from './styles.css' - -const EditModeHeader = ({ - setEditMode, -}: { - setEditMode: (enable: boolean) => any -}) => { - return ( - <> -

- -

- - - - - - ) -} - -export default EditModeHeader diff --git a/src/views/ArticleDetail/EditModeSidebar/EditCollection.tsx b/src/views/ArticleDetail/EditModeSidebar/EditCollection.tsx deleted file mode 100644 index 7b9e1bef9d..0000000000 --- a/src/views/ArticleDetail/EditModeSidebar/EditCollection.tsx +++ /dev/null @@ -1,118 +0,0 @@ -import { useQuery } from '@apollo/react-hooks' -import gql from 'graphql-tag' -import _uniq from 'lodash/uniq' - -import { Spinner, Translate } from '~/components' -import SidebarCollection from '~/components/Editor/Sidebar/Collection' -import { QueryError, useMutation } from '~/components/GQL' -import articleFragments from '~/components/GQL/fragments/article' - -import { ADD_TOAST } from '~/common/enums' - -import { ArticleDigestDropdownArticle } from '~/components/ArticleDigest/Dropdown/__generated__/ArticleDigestDropdownArticle' -import { ArticleCollection } from './__generated__/ArticleCollection' -import { EditCollectionArticle } from './__generated__/EditCollectionArticle' -import { SetArticleCollection } from './__generated__/SetArticleCollection' - -interface EditCollectionProps { - article: EditCollectionArticle -} - -const fragments = { - article: gql` - fragment EditCollectionArticle on Article { - id - } - `, -} - -const ARTICLE_COLLECTION = gql` - query ArticleCollection($id: ID!, $after: String, $first: Int = null) { - node(input: { id: $id }) { - id - ... on Article { - ...ArticleCollection - } - } - } - ${articleFragments.articleCollection} -` - -/** - * Note: - * - * The response of this mutation is aligned with `COLLECTION_LIST` in `CollectionList.tsx`, - * so that it will auto update the local cache and prevent refetch logics - */ -const SET_ARTICLE_COLLECTION = gql` - mutation SetArticleCollection( - $id: ID! - $after: String - $first: Int - $collection: [ID!]! - ) { - setCollection(input: { id: $id, collection: $collection }) { - ...ArticleCollection - } - } - ${articleFragments.articleCollection} -` - -const EditCollection = ({ article }: EditCollectionProps) => { - const { data, loading, error } = useQuery( - ARTICLE_COLLECTION, - { - variables: { id: article.id }, - } - ) - - const [setCollection] = useMutation( - SET_ARTICLE_COLLECTION - ) - - const handleCollectionChange = async ( - articles: ArticleDigestDropdownArticle[] - ) => { - try { - await setCollection({ - variables: { - id: article.id, - collection: _uniq(articles.map((item: any) => item.id)), - first: null, - }, - }) - } catch (e) { - window.dispatchEvent( - new CustomEvent(ADD_TOAST, { - detail: { - color: 'red', - content: , - }, - }) - ) - } - } - - if (loading) { - return - } - - if (error) { - return - } - - return ( - node)) || - [] - } - onEdit={handleCollectionChange} - /> - ) -} - -EditCollection.fragments = fragments - -export default EditCollection diff --git a/src/views/ArticleDetail/EditModeSidebar/index.tsx b/src/views/ArticleDetail/EditModeSidebar/index.tsx deleted file mode 100644 index 3fdd41f4ac..0000000000 --- a/src/views/ArticleDetail/EditModeSidebar/index.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import gql from 'graphql-tag' - -import EditCollection from './EditCollection' - -import { EditModeSidebarArticle } from './__generated__/EditModeSidebarArticle' - -interface EditModeSidebarProps { - article: EditModeSidebarArticle -} - -const EditModeSidebar = ({ article }: EditModeSidebarProps) => ( - <> - - -) - -EditModeSidebar.fragments = { - article: gql` - fragment EditModeSidebarArticle on Article { - id - ...EditCollectionArticle - } - ${EditCollection.fragments.article} - `, -} - -export default EditModeSidebar diff --git a/src/views/ArticleDetail/index.tsx b/src/views/ArticleDetail/index.tsx index ef9b2742b7..eb934f8677 100644 --- a/src/views/ArticleDetail/index.tsx +++ b/src/views/ArticleDetail/index.tsx @@ -29,8 +29,7 @@ import { getQuery } from '~/common/utils' import Collection from './Collection' import Content from './Content' -import EditModeHeader from './EditModeHeader' -import EditModeSidebar from './EditModeSidebar' +import EditMode from './EditMode' import FingerprintButton from './FingerprintButton' import { ARTICLE_DETAIL_PRIVATE, @@ -45,6 +44,7 @@ import Toolbar from './Toolbar' import TranslationButton from './TranslationButton' import Wall from './Wall' +import { ArticleDigestDropdownArticle } from '~/components/ArticleDigest/Dropdown/__generated__/ArticleDigestDropdownArticle' import { ClientPreference } from '~/components/GQL/queries/__generated__/ClientPreference' import { ArticleDetailPublic } from './__generated__/ArticleDetailPublic' import { ArticleTranslation } from './__generated__/ArticleTranslation' @@ -77,6 +77,10 @@ const ArticleDetail = () => { // edit mode const [editMode, setEditMode] = useState(false) + const [editModeTags, setEditModeTags] = useState([]) + const [editModeCollection, setEditModeCollection] = useState< + ArticleDigestDropdownArticle[] + >([]) // wall const { data: clientPreferenceData } = useQuery( @@ -188,6 +192,9 @@ const ArticleDetail = () => { ) } + /** + * Archived or Banned + */ if (article.state !== 'active' && viewer.id !== authorId) { return ( @@ -213,10 +220,29 @@ const ArticleDetail = () => { ) } + /** + * Edit Mode + */ + const sidebarProps = { + articleId: article.id, + tags: editModeTags, + collection: editModeCollection, + setTags: setEditModeTags, + setCollection: setEditModeCollection, + } if (editMode) { return ( - }> - } /> + }> + + } + /> {isLargeUp && (
@@ -233,7 +259,7 @@ const ArticleDetail = () => { {!isLargeUp && ( - + )} From 9016255a7599bdb82130398fc4d2a30607bfabf6 Mon Sep 17 00:00:00 2001 From: RoberTu Date: Tue, 7 Jul 2020 22:04:40 +0800 Subject: [PATCH 07/86] feat(edit-article): editing tags of article --- .../DropdownActions/RemoveTagButton.tsx | 2 +- .../DropdownActions/SetTagSelectedButton.tsx | 2 +- .../SetTagUnselectedButton.tsx | 2 +- .../ArticleDetail/EditMode/Header/index.tsx | 16 ++++- .../EditMode/Sidebar/EditTags.tsx | 62 +++++++++++++++++++ .../ArticleDetail/EditMode/Sidebar/index.tsx | 7 +-- src/views/ArticleDetail/TagList/index.tsx | 2 +- src/views/ArticleDetail/index.tsx | 1 + 8 files changed, 83 insertions(+), 11 deletions(-) create mode 100644 src/views/ArticleDetail/EditMode/Sidebar/EditTags.tsx diff --git a/src/components/ArticleDigest/DropdownActions/RemoveTagButton.tsx b/src/components/ArticleDigest/DropdownActions/RemoveTagButton.tsx index cb5d431420..df04f3db02 100644 --- a/src/components/ArticleDigest/DropdownActions/RemoveTagButton.tsx +++ b/src/components/ArticleDigest/DropdownActions/RemoveTagButton.tsx @@ -25,7 +25,7 @@ const fragments = { article: gql` fragment RemoveTagButtonArticle on Article { id - tags { + tags @connection(key: "tagsList") { id creator { id diff --git a/src/components/ArticleDigest/DropdownActions/SetTagSelectedButton.tsx b/src/components/ArticleDigest/DropdownActions/SetTagSelectedButton.tsx index 2f4efc57a2..8d19e4c1ee 100644 --- a/src/components/ArticleDigest/DropdownActions/SetTagSelectedButton.tsx +++ b/src/components/ArticleDigest/DropdownActions/SetTagSelectedButton.tsx @@ -25,7 +25,7 @@ const fragments = { article: gql` fragment SetTagSelectedButtonArticle on Article { id - tags { + tags @connection(key: "tagsList") { id creator { id diff --git a/src/components/ArticleDigest/DropdownActions/SetTagUnselectedButton.tsx b/src/components/ArticleDigest/DropdownActions/SetTagUnselectedButton.tsx index 2f92fd1c28..a0ec925e60 100644 --- a/src/components/ArticleDigest/DropdownActions/SetTagUnselectedButton.tsx +++ b/src/components/ArticleDigest/DropdownActions/SetTagUnselectedButton.tsx @@ -31,7 +31,7 @@ const fragments = { article: gql` fragment SetTagUnselectedButtonArticle on Article { id - tags { + tags @connection(key: "tagsList") { id creator { id diff --git a/src/views/ArticleDetail/EditMode/Header/index.tsx b/src/views/ArticleDetail/EditMode/Header/index.tsx index 7bd646bd9b..50192529ba 100644 --- a/src/views/ArticleDetail/EditMode/Header/index.tsx +++ b/src/views/ArticleDetail/EditMode/Header/index.tsx @@ -1,7 +1,7 @@ import gql from 'graphql-tag' import _uniq from 'lodash/uniq' -import { Button, IconSpinner, TextIcon, Translate } from '~/components' +import { Button, IconSpinner, Tag, TextIcon, Translate } from '~/components' import { useMutation } from '~/components/GQL' import articleFragments from '~/components/GQL/fragments/article' @@ -14,6 +14,7 @@ import { EditArticle } from './__generated__/EditArticle' interface EditModeHeaderProps { id: string + mediaHash: string tags: string[] collection: ArticleDigestDropdownArticle[] setEditMode: (enable: boolean) => any @@ -25,24 +26,31 @@ interface EditModeHeaderProps { * The response of this mutation is aligned with `COLLECTION_LIST` in `CollectionList.tsx`, * so that it will auto update the local cache and prevent refetch logics */ - const EDIT_ARTICLE = gql` mutation EditArticle( $id: ID! + $mediaHash: String! + $tags: [String!] $collection: [ID!] $after: String $first: Int = null ) { - editArticle(input: { id: $id, collection: $collection }) { + editArticle(input: { id: $id, tags: $tags, collection: $collection }) { id + tags @connection(key: "tagsList") { + ...DigestTag + selected(input: { mediaHash: $mediaHash }) + } ...ArticleCollection } } + ${Tag.fragments.tag} ${articleFragments.articleCollection} ` const EditModeHeader = ({ id, + mediaHash, tags, collection, setEditMode, @@ -54,6 +62,8 @@ const EditModeHeader = ({ await editArticle({ variables: { id, + mediaHash, + tags, collection: _uniq(collection.map(({ id: articleId }) => articleId)), first: null, }, diff --git a/src/views/ArticleDetail/EditMode/Sidebar/EditTags.tsx b/src/views/ArticleDetail/EditMode/Sidebar/EditTags.tsx new file mode 100644 index 0000000000..22055cba84 --- /dev/null +++ b/src/views/ArticleDetail/EditMode/Sidebar/EditTags.tsx @@ -0,0 +1,62 @@ +import { useQuery } from '@apollo/react-hooks' +import gql from 'graphql-tag' +import _uniq from 'lodash/uniq' +import { useEffect } from 'react' + +import { Spinner, Tag } from '~/components' +import SidebarTags from '~/components/Editor/Sidebar/Tags' +import { QueryError } from '~/components/GQL' + +import { ArticleTags } from './__generated__/ArticleTags' + +export interface EditTagsProps { + articleId: string + tags: string[] + setTags: (tags: string[]) => any +} + +const ARTICLE_TAGS = gql` + query ArticleTags($id: ID!) { + node(input: { id: $id }) { + id + ... on Article { + tags @connection(key: "tagsList") { + ...DigestTag + } + } + } + } + ${Tag.fragments.tag} +` + +const EditTags = ({ articleId, tags, setTags }: EditTagsProps) => { + const { data, loading, error } = useQuery(ARTICLE_TAGS, { + variables: { id: articleId }, + }) + + useEffect(() => { + setTags( + (data?.node?.__typename === 'Article' && + data.node.tags?.map(({ content }) => content)) || + [] + ) + }, [data?.node?.__typename]) + + if (loading) { + return + } + + if (error) { + return + } + + return ( + setTags(_uniq(tags.concat(tag)))} + onDeleteTag={(tag) => setTags(tags.filter((it) => it !== tag))} + /> + ) +} + +export default EditTags diff --git a/src/views/ArticleDetail/EditMode/Sidebar/index.tsx b/src/views/ArticleDetail/EditMode/Sidebar/index.tsx index fd44d1f4f8..13207335a7 100644 --- a/src/views/ArticleDetail/EditMode/Sidebar/index.tsx +++ b/src/views/ArticleDetail/EditMode/Sidebar/index.tsx @@ -1,12 +1,11 @@ import EditCollection, { EditCollectionProps } from './EditCollection' +import EditTags, { EditTagsProps } from './EditTags' -type EditModeSidebarProps = { - tags: string[] - setTags: (tags: string[]) => any -} & EditCollectionProps +type EditModeSidebarProps = EditTagsProps & EditCollectionProps const EditModeSidebar = (props: EditModeSidebarProps) => ( <> + ) diff --git a/src/views/ArticleDetail/TagList/index.tsx b/src/views/ArticleDetail/TagList/index.tsx index 67b79bc444..921be23d1d 100644 --- a/src/views/ArticleDetail/TagList/index.tsx +++ b/src/views/ArticleDetail/TagList/index.tsx @@ -9,7 +9,7 @@ import { TagListArticle } from './__generated__/TagListArticle' const fragments = { article: gql` fragment TagListArticle on Article { - tags { + tags @connection(key: "tagsList") { ...DigestTag selected(input: { mediaHash: $mediaHash }) } diff --git a/src/views/ArticleDetail/index.tsx b/src/views/ArticleDetail/index.tsx index eb934f8677..0fcfc3dc01 100644 --- a/src/views/ArticleDetail/index.tsx +++ b/src/views/ArticleDetail/index.tsx @@ -237,6 +237,7 @@ const ArticleDetail = () => { right={ Date: Wed, 8 Jul 2020 09:47:27 +0800 Subject: [PATCH 08/86] feat(component): revise tag detail page for follow and unfollow --- src/common/enums/text.ts | 4 + src/common/gql/fragmentTypes.json | 2 +- .../DropdownActions/RemoveTagButton.tsx | 14 ++- src/components/Button/index.tsx | 12 ++- .../GQL/updates/tagArticlesCount.ts | 53 ++++++++++ src/components/GQL/updates/tagFollowers.ts | 8 +- .../Latest/index.tsx | 0 .../Selected/index.tsx | 0 .../{TagDetailArticles => Articles}/index.tsx | 0 src/views/TagDetail/ArticlesCount/index.tsx | 39 +++++++ src/views/TagDetail/ArticlesCount/styles.css | 8 ++ .../AddButton}/TagArticleDialog/Content.tsx | 10 +- .../AddButton}/TagArticleDialog/index.tsx | 9 +- .../AddButton}/TagArticleDialog/styles.css | 0 .../TagDetail/Buttons/AddButton/index.tsx | 100 ++++++++++++++++++ .../TagDetail/Buttons/FollowButton/Follow.tsx | 71 +++++++++++++ .../Buttons/FollowButton/Unfollow.tsx | 67 ++++++++++++ .../TagDetail/Buttons/FollowButton/index.tsx | 31 ++++++ src/views/TagDetail/Buttons/index.tsx | 7 ++ src/views/TagDetail/DropdownActions/index.tsx | 99 +++++++++++++++++ src/views/TagDetail/Followers/index.tsx | 58 ++++++++++ src/views/TagDetail/Followers/styles.css | 51 +++++++++ .../AddArticleButton/index.tsx | 29 ----- .../TagDetailButtons/EditTagButton/index.tsx | 31 ------ .../TagDetail/TagDetailButtons/index.tsx | 7 -- src/views/TagDetail/index.tsx | 78 ++++++++------ src/views/TagDetail/styles.css | 26 +++-- 27 files changed, 692 insertions(+), 122 deletions(-) create mode 100644 src/components/GQL/updates/tagArticlesCount.ts rename src/views/TagDetail/{TagDetailArticles => Articles}/Latest/index.tsx (100%) rename src/views/TagDetail/{TagDetailArticles => Articles}/Selected/index.tsx (100%) rename src/views/TagDetail/{TagDetailArticles => Articles}/index.tsx (100%) create mode 100644 src/views/TagDetail/ArticlesCount/index.tsx create mode 100644 src/views/TagDetail/ArticlesCount/styles.css rename src/views/TagDetail/{TagDetailButtons/AddArticleButton => Buttons/AddButton}/TagArticleDialog/Content.tsx (94%) rename src/views/TagDetail/{TagDetailButtons/AddArticleButton => Buttons/AddButton}/TagArticleDialog/index.tsx (79%) rename src/views/TagDetail/{TagDetailButtons/AddArticleButton => Buttons/AddButton}/TagArticleDialog/styles.css (100%) create mode 100644 src/views/TagDetail/Buttons/AddButton/index.tsx create mode 100644 src/views/TagDetail/Buttons/FollowButton/Follow.tsx create mode 100644 src/views/TagDetail/Buttons/FollowButton/Unfollow.tsx create mode 100644 src/views/TagDetail/Buttons/FollowButton/index.tsx create mode 100644 src/views/TagDetail/Buttons/index.tsx create mode 100644 src/views/TagDetail/DropdownActions/index.tsx create mode 100644 src/views/TagDetail/Followers/index.tsx create mode 100644 src/views/TagDetail/Followers/styles.css delete mode 100644 src/views/TagDetail/TagDetailButtons/AddArticleButton/index.tsx delete mode 100644 src/views/TagDetail/TagDetailButtons/EditTagButton/index.tsx delete mode 100644 src/views/TagDetail/TagDetailButtons/index.tsx diff --git a/src/common/enums/text.ts b/src/common/enums/text.ts index 105c3a701b..24a111614d 100644 --- a/src/common/enums/text.ts +++ b/src/common/enums/text.ts @@ -234,6 +234,8 @@ export const TEXT = { tagDescriptionPlaceholder: '輸入一段標籤描述…', tagEdited: '標籤已更新', tagName: '標籤名稱', + tagAddArticle: '添加已有作品', + tagAddSelectedArticle: '添加精選內容', term: '用戶協議', termAndPrivacy: '用戶協議與隱私政策', termHint: '我們的用戶協議和隱私政策發生了更改,請閱讀並同意後繼續使用。', @@ -506,6 +508,8 @@ export const TEXT = { tagDescriptionPlaceholder: '输入一段话题描述…', tagEdited: '标签已更新', tagName: '标签名称', + tagAddArticle: '添加已有作品', + tagAddSelectedArticle: '添加精选內容', term: '用户协议', termAndPrivacy: '用户协议与隐私政策', termHint: '我们的用户协议和隐私政策发生了更改,请阅读并同意后继续使用。', diff --git a/src/common/gql/fragmentTypes.json b/src/common/gql/fragmentTypes.json index fa7b25c222..b715c5daa1 100644 --- a/src/common/gql/fragmentTypes.json +++ b/src/common/gql/fragmentTypes.json @@ -17,8 +17,8 @@ "name": "Connection", "possibleTypes": [ { "name": "ArticleConnection" }, - { "name": "ResponseConnection" }, { "name": "CommentConnection" }, + { "name": "ResponseConnection" }, { "name": "TagConnection" }, { "name": "UserConnection" }, { "name": "DraftConnection" }, diff --git a/src/components/ArticleDigest/DropdownActions/RemoveTagButton.tsx b/src/components/ArticleDigest/DropdownActions/RemoveTagButton.tsx index a98e5d4203..6ca8260962 100644 --- a/src/components/ArticleDigest/DropdownActions/RemoveTagButton.tsx +++ b/src/components/ArticleDigest/DropdownActions/RemoveTagButton.tsx @@ -4,8 +4,10 @@ import { useRouter } from 'next/router' import { IconRemoveMedium, Menu, TextIcon, Translate } from '~/components' import { useMutation } from '~/components/GQL' +import updateTagArticlesCount from '~/components/GQL/updates/tagArticlesCount' import { REFETCH_TAG_DETAIL_ARTICLES } from '~/common/enums' +import { getQuery } from '~/common/utils' import { DeleteArticlesTags } from './__generated__/DeleteArticlesTags' import { RemoveTagButtonArticle } from './__generated__/RemoveTagButtonArticle' @@ -40,14 +42,16 @@ const fragments = { const RemoveTagButton = ({ article }: { article: RemoveTagButtonArticle }) => { const router = useRouter() - const { - query: { id }, - } = router - const tagId = _isArray(id) ? id[0] : id + const id = getQuery({ router, key: 'tagId' }) const [deleteArticlesTags] = useMutation( DELETE_ARTICLES_TAGS, - { variables: { id: tagId, articles: [article.id] } } + { + variables: { id, articles: [article.id] }, + update: (cache) => { + updateTagArticlesCount({ cache, type: 'decrement', id }) + }, + } ) return ( diff --git a/src/components/Button/index.tsx b/src/components/Button/index.tsx index 62fa55dab2..73e3f85873 100644 --- a/src/components/Button/index.tsx +++ b/src/components/Button/index.tsx @@ -54,7 +54,10 @@ type ButtonColor = | 'gold' | 'red' -type ButtonTextColor = Extract +type ButtonTextColor = Extract< + ButtonColor, + 'white' | 'black' | 'green' | 'gold' | 'red' +> export type ButtonBgColor = Extract< ButtonColor, @@ -70,7 +73,12 @@ export type ButtonBgColor = Extract< type ButtonBgActiveColor = Extract< ButtonColor, - 'grey-lighter' | 'green-lighter' | 'grey-lighter-active' | 'green' | 'gold' | 'red' + | 'grey-lighter' + | 'green-lighter' + | 'grey-lighter-active' + | 'green' + | 'gold' + | 'red' > export interface ButtonProps { diff --git a/src/components/GQL/updates/tagArticlesCount.ts b/src/components/GQL/updates/tagArticlesCount.ts new file mode 100644 index 0000000000..a134537889 --- /dev/null +++ b/src/components/GQL/updates/tagArticlesCount.ts @@ -0,0 +1,53 @@ +import { DataProxy } from 'apollo-cache' + +import TAG_ARTICLES_COUNT from '~/components/GQL/queries/tagArticlesCount' + +import { ERROR_CODES } from '~/common/enums' + +import { TagArticlesCount } from '~/components/GQL/queries/__generated__/TagArticlesCount' + +const update = ({ + cache, + id, + type, +}: { + cache: DataProxy + id: string + type: 'increment' | 'decrement' +}) => { + try { + if (!id) { + return + } + + const variables = { id } + const cacheData = cache.readQuery({ + query: TAG_ARTICLES_COUNT, + variables, + }) + + if (!cacheData || !cacheData.node || cacheData.node.__typename !== 'Tag') { + return + } + + if (type === 'increment') { + cacheData.node.articles.totalCount++ + } else { + cacheData.node.articles.totalCount-- + } + + cache.writeQuery({ + query: TAG_ARTICLES_COUNT, + variables, + data: cacheData, + }) + } catch (e) { + if (e.message.startsWith("Can't find field")) { + console.warn(ERROR_CODES.QUERY_FIELD_NOT_FOUND) + } else { + console.error(e) + } + } +} + +export default update diff --git a/src/components/GQL/updates/tagFollowers.ts b/src/components/GQL/updates/tagFollowers.ts index 63ac7365a6..70008ab9a3 100644 --- a/src/components/GQL/updates/tagFollowers.ts +++ b/src/components/GQL/updates/tagFollowers.ts @@ -34,8 +34,8 @@ const update = ({ const followers = cacheData.node.followers.edges || [] if (type === 'follow') { - followers.push({ - cursor: window.btoa(`arrayconnection:${followers.length}:0`) || '', + followers.unshift({ + cursor: window.btoa(`arrayconnection:${followers.length}:0`) || '', node: { avatar: viewer.avatar, id: viewer.id, @@ -50,7 +50,9 @@ const update = ({ cacheData.node.followers.edges = followers cacheData.node.followers.totalCount++ } else { - cacheData.node.followers.edges = followers.filter(follower => follower.node.id !== viewer.id) + cacheData.node.followers.edges = followers.filter( + (follower) => follower.node.id !== viewer.id + ) cacheData.node.followers.totalCount-- } diff --git a/src/views/TagDetail/TagDetailArticles/Latest/index.tsx b/src/views/TagDetail/Articles/Latest/index.tsx similarity index 100% rename from src/views/TagDetail/TagDetailArticles/Latest/index.tsx rename to src/views/TagDetail/Articles/Latest/index.tsx diff --git a/src/views/TagDetail/TagDetailArticles/Selected/index.tsx b/src/views/TagDetail/Articles/Selected/index.tsx similarity index 100% rename from src/views/TagDetail/TagDetailArticles/Selected/index.tsx rename to src/views/TagDetail/Articles/Selected/index.tsx diff --git a/src/views/TagDetail/TagDetailArticles/index.tsx b/src/views/TagDetail/Articles/index.tsx similarity index 100% rename from src/views/TagDetail/TagDetailArticles/index.tsx rename to src/views/TagDetail/Articles/index.tsx diff --git a/src/views/TagDetail/ArticlesCount/index.tsx b/src/views/TagDetail/ArticlesCount/index.tsx new file mode 100644 index 0000000000..b75d214ebf --- /dev/null +++ b/src/views/TagDetail/ArticlesCount/index.tsx @@ -0,0 +1,39 @@ +import { useQuery } from '@apollo/react-hooks' + +import { Translate } from '~/components' +import TAG_ARTICLES_COUNT from '~/components/GQL/queries/tagArticlesCount' + +import { numAbbr } from '~/common/utils' + +import styles from './styles.css' + +import { TagArticlesCount } from '~/components/GQL/queries/__generated__/TagArticlesCount' + +interface ArticlesCountProps { + id: string +} + +const ArticlesCount = ({ id }: ArticlesCountProps) => { + const { data } = useQuery(TAG_ARTICLES_COUNT, { + variables: { id }, + }) + + if (!data || !data.node || data.node.__typename !== 'Tag') { + return null + } + + const { totalCount } = data?.node?.articles || { totalCount: 0 } + + return ( +
+ {numAbbr(totalCount)} + + + + + +
+ ) +} + +export default ArticlesCount diff --git a/src/views/TagDetail/ArticlesCount/styles.css b/src/views/TagDetail/ArticlesCount/styles.css new file mode 100644 index 0000000000..0393892aac --- /dev/null +++ b/src/views/TagDetail/ArticlesCount/styles.css @@ -0,0 +1,8 @@ +.container { + font-size: var(--font-size-sm); + color: var(--color-grey-darker); + + & b { + color: var(--color-black); + } +} diff --git a/src/views/TagDetail/TagDetailButtons/AddArticleButton/TagArticleDialog/Content.tsx b/src/views/TagDetail/Buttons/AddButton/TagArticleDialog/Content.tsx similarity index 94% rename from src/views/TagDetail/TagDetailButtons/AddArticleButton/TagArticleDialog/Content.tsx rename to src/views/TagDetail/Buttons/AddButton/TagArticleDialog/Content.tsx index 84470d6752..dd74abfba3 100644 --- a/src/views/TagDetail/TagDetailButtons/AddArticleButton/TagArticleDialog/Content.tsx +++ b/src/views/TagDetail/Buttons/AddButton/TagArticleDialog/Content.tsx @@ -24,10 +24,10 @@ import styles from './styles.css' import { PutArticlesTags } from './__generated__/PutArticlesTags' const PUT_ARTICLES_TAGS = gql` - mutation PutArticlesTags($id: ID!, $articles: [ID!]) { + mutation PutArticlesTags($id: ID!, $articles: [ID!], $selected: Boolean!) { putArticlesTags(input: { id: $id, articles: $articles }) { id - articles(input: { first: 0, selected: true }) { + articles(input: { first: 0, selected: $selected }) { totalCount } } @@ -60,6 +60,7 @@ const DropdownContent = ({ interface TagArticleDialogContentProps { id?: string closeDialog: () => void + forSelected?: boolean } interface FormValues { @@ -70,6 +71,7 @@ interface FormValues { const TagArticleDialogContent: React.FC = ({ closeDialog, id, + forSelected = false, }) => { const isSmallUp = useResponsive('sm-up') const [selectedArticles, setSelectedArticles] = useState([]) @@ -110,7 +112,7 @@ const TagArticleDialogContent: React.FC = ({ return } - await update({ variables: { id, articles } }) + await update({ variables: { id, articles, selected: forSelected } }) setSubmitting(false) @@ -226,7 +228,7 @@ const TagArticleDialogContent: React.FC = ({ return ( <> diff --git a/src/views/TagDetail/TagDetailButtons/AddArticleButton/TagArticleDialog/index.tsx b/src/views/TagDetail/Buttons/AddButton/TagArticleDialog/index.tsx similarity index 79% rename from src/views/TagDetail/TagDetailButtons/AddArticleButton/TagArticleDialog/index.tsx rename to src/views/TagDetail/Buttons/AddButton/TagArticleDialog/index.tsx index cdb856edb6..4f21e37640 100644 --- a/src/views/TagDetail/TagDetailButtons/AddArticleButton/TagArticleDialog/index.tsx +++ b/src/views/TagDetail/Buttons/AddButton/TagArticleDialog/index.tsx @@ -7,9 +7,14 @@ import Content from './Content' interface TagArticleDialogProps { id?: string children: ({ open }: { open: () => void }) => React.ReactNode + forSelected?: boolean } -const TagArticleDialog = ({ id, children }: TagArticleDialogProps) => { +const TagArticleDialog = ({ + id, + children, + forSelected, +}: TagArticleDialogProps) => { const [showDialog, setShowDialog] = useState(true) const open = () => setShowDialog(true) const close = () => setShowDialog(false) @@ -19,7 +24,7 @@ const TagArticleDialog = ({ id, children }: TagArticleDialogProps) => { {children({ open })} - + ) diff --git a/src/views/TagDetail/TagDetailButtons/AddArticleButton/TagArticleDialog/styles.css b/src/views/TagDetail/Buttons/AddButton/TagArticleDialog/styles.css similarity index 100% rename from src/views/TagDetail/TagDetailButtons/AddArticleButton/TagArticleDialog/styles.css rename to src/views/TagDetail/Buttons/AddButton/TagArticleDialog/styles.css diff --git a/src/views/TagDetail/Buttons/AddButton/index.tsx b/src/views/TagDetail/Buttons/AddButton/index.tsx new file mode 100644 index 0000000000..2ed4da53e8 --- /dev/null +++ b/src/views/TagDetail/Buttons/AddButton/index.tsx @@ -0,0 +1,100 @@ +import { + Button, + DropdownDialog, + IconAddMedium, + IconHashTag, + IconPen, + Menu, + TextIcon, + Translate, +} from '~/components' + +import TagArticleDialog from './TagArticleDialog' + +interface DropdownActionsProps { + id: string +} + +interface DialogProps { + openTagSelectedArticleDialog: () => void + openTagArticleDialog: () => void +} + +type BaseDropdownActionsProps = DropdownActionsProps & DialogProps + +const BaseDropdownActions = ({ + id, + openTagSelectedArticleDialog, + openTagArticleDialog, +}: BaseDropdownActionsProps) => { + const Content = ({ isInDropdown }: { isInDropdown?: boolean }) => ( + + + } size="md" spacing="base"> + + + + + + } size="md" spacing="base"> + + + + + } size="md" spacing="base"> + + + + + ) + + return ( + , + placement: 'bottom-end', + }} + dialog={{ + content: , + title: 'moreActions', + }} + > + {({ open, ref }) => ( + + )} + + ) +} + +const DropdownActions = (props: DropdownActionsProps) => { + return ( + + {({ open: openTagSelectedArticleDialog }) => ( + + {({ open: openTagArticleDialog }) => ( + + )} + + )} + + ) +} + +export default DropdownActions diff --git a/src/views/TagDetail/Buttons/FollowButton/Follow.tsx b/src/views/TagDetail/Buttons/FollowButton/Follow.tsx new file mode 100644 index 0000000000..2f0f1624b6 --- /dev/null +++ b/src/views/TagDetail/Buttons/FollowButton/Follow.tsx @@ -0,0 +1,71 @@ +import gql from 'graphql-tag' +import _isNil from 'lodash/isNil' +import { useContext } from 'react' + +import { + Button, + IconAdd, + TextIcon, + Translate, + ViewerContext, +} from '~/components' +import { useMutation } from '~/components/GQL' +import updateTagFollowers from '~/components/GQL/updates/tagFollowers' + +import { FollowButtonTag as FollowButtonTagType } from './__generated__/FollowButtonTag' +import { FollowTag } from './__generated__/FollowTag' + +interface FollowProps { + tag: FollowButtonTagType +} + +const FOLLOW_TAG = gql` + mutation FollowTag($id: ID!) { + toggleFollowTag(input: { id: $id }) { + id + isFollower + } + } +` + +const Follow = ({ tag }: FollowProps) => { + const viewer = useContext(ViewerContext) + const [follow] = useMutation(FOLLOW_TAG, { + variables: { id: tag.id }, + optimisticResponse: + !_isNil(tag.id) && !_isNil(tag.isFollower) + ? { + toggleFollowTag: { + id: tag.id, + isFollower: true, + __typename: 'Tag', + }, + } + : undefined, + update: (cache) => { + updateTagFollowers({ + cache, + id: tag.id, + type: 'follow', + viewer, + }) + }, + }) + + return ( + + ) +} + +export default Follow diff --git a/src/views/TagDetail/Buttons/FollowButton/Unfollow.tsx b/src/views/TagDetail/Buttons/FollowButton/Unfollow.tsx new file mode 100644 index 0000000000..83765fd3a2 --- /dev/null +++ b/src/views/TagDetail/Buttons/FollowButton/Unfollow.tsx @@ -0,0 +1,67 @@ +import gql from 'graphql-tag' +import _isNil from 'lodash/isNil' +import { useContext, useState } from 'react' + +import { Button, TextIcon, Translate, ViewerContext } from '~/components' +import { useMutation } from '~/components/GQL' +import updateTagFollowers from '~/components/GQL/updates/tagFollowers' + +import { FollowButtonTag as FollowButtonTagType } from './__generated__/FollowButtonTag' +import { UnfollowTag } from './__generated__/UnfollowTag' + +interface UnfollowTagProps { + tag: FollowButtonTagType +} + +const UNFOLLOW_TAG = gql` + mutation UnfollowTag($id: ID!) { + toggleFollowTag(input: { id: $id }) { + id + isFollower + } + } +` + +const Unfollow = ({ tag }: UnfollowTagProps) => { + const viewer = useContext(ViewerContext) + const [hover, setHover] = useState(false) + const [unfollow] = useMutation(UNFOLLOW_TAG, { + variables: { id: tag.id }, + optimisticResponse: + !_isNil(tag.id) && !_isNil(tag.isFollower) + ? { + toggleFollowTag: { + id: tag.id, + isFollower: false, + __typename: 'Tag', + }, + } + : undefined, + update: (cache) => { + updateTagFollowers({ + cache, + type: 'unfollow', + id: tag.id, + viewer, + }) + }, + }) + + return ( + + ) +} + +export default Unfollow diff --git a/src/views/TagDetail/Buttons/FollowButton/index.tsx b/src/views/TagDetail/Buttons/FollowButton/index.tsx new file mode 100644 index 0000000000..3c6ef884b0 --- /dev/null +++ b/src/views/TagDetail/Buttons/FollowButton/index.tsx @@ -0,0 +1,31 @@ +import gql from 'graphql-tag' + +import Follow from './Follow' +import Unfollow from './Unfollow' + +import { FollowButtonTag as FollowButtonTagType } from './__generated__/FollowButtonTag' + +interface FollowButtonProps { + tag: FollowButtonTagType +} + +const fragments = { + tag: gql` + fragment FollowButtonTag on Tag { + id + isFollower + } + `, +} + +const FollowButton = ({ tag }: FollowButtonProps) => { + if (tag.isFollower) { + return + } else { + return + } +} + +FollowButton.fragments = fragments + +export default FollowButton diff --git a/src/views/TagDetail/Buttons/index.tsx b/src/views/TagDetail/Buttons/index.tsx new file mode 100644 index 0000000000..7c8d964ce8 --- /dev/null +++ b/src/views/TagDetail/Buttons/index.tsx @@ -0,0 +1,7 @@ +import AddButton from './AddButton' +import FollowButton from './FollowButton' + +export const TagDetailButtons = { + AddButton, + FollowButton, +} diff --git a/src/views/TagDetail/DropdownActions/index.tsx b/src/views/TagDetail/DropdownActions/index.tsx new file mode 100644 index 0000000000..b013059e62 --- /dev/null +++ b/src/views/TagDetail/DropdownActions/index.tsx @@ -0,0 +1,99 @@ +import { + Button, + DropdownDialog, + IconEdit, + IconMoreLarge, + IconShare, + Menu, + ShareDialog, + TagDialog, + TextIcon, + Translate, +} from '~/components' + +import { TEXT } from '~/common/enums' + +interface DropdownActionsProps { + id: string + content?: string + description?: string + canEdit: boolean +} + +interface DialogProps { + openShareDialog: () => void + openTagDialog: () => void +} + +type BaseDropdownActionsProps = DropdownActionsProps & DialogProps + +const BaseDropdownActions = ({ + id, + content, + description, + canEdit, + openShareDialog, + openTagDialog, +}: BaseDropdownActionsProps) => { + const Content = ({ isInDropdown }: { isInDropdown?: boolean }) => ( + + + } size="md" spacing="base"> + + + + {canEdit && ( + + } size="md" spacing="base"> + + + + )} + + ) + + return ( + , + placement: 'bottom-end', + }} + dialog={{ + content: , + title: 'moreActions', + }} + > + {({ open, ref }) => ( + + )} + + ) +} + +const DropdownActions = (props: DropdownActionsProps) => { + return ( + + {({ open: openShareDialog }) => ( + + {({ open: openTagDialog }) => ( + + )} + + )} + + ) +} + +export default DropdownActions diff --git a/src/views/TagDetail/Followers/index.tsx b/src/views/TagDetail/Followers/index.tsx new file mode 100644 index 0000000000..39c28006b3 --- /dev/null +++ b/src/views/TagDetail/Followers/index.tsx @@ -0,0 +1,58 @@ +import { useQuery } from '@apollo/react-hooks' + +import { Translate } from '~/components' +import { Avatar } from '~/components/Avatar' +import TAG_FOLLOWERS from '~/components/GQL/queries/tagFollowers' + +import { IMAGE_PIXEL } from '~/common/enums' +import { numAbbr } from '~/common/utils' + +import styles from './styles.css' + +import { TagFollowers } from '~/components/GQL/queries/__generated__/TagFollowers' + +interface FollowersProps { + id: string +} + +const Followers = ({ id }: FollowersProps) => { + const { data } = useQuery(TAG_FOLLOWERS, { variables: { id } }) + + if (!data || !data.node || data.node.__typename !== 'Tag') { + return null + } + + const { edges, totalCount } = data?.node?.followers || { + edges: [], + totalCount: 0, + } + const followers = ( + edges?.map(({ node }) => node).filter((user) => !!user) || [] + ).slice(0, 5) + + return ( +
+
+ {followers.map((user, index) => ( + + ))} +
+ +
+ {numAbbr(totalCount)} + + + +
+ + +
+ ) +} + +export default Followers diff --git a/src/views/TagDetail/Followers/styles.css b/src/views/TagDetail/Followers/styles.css new file mode 100644 index 0000000000..71fd6de2b8 --- /dev/null +++ b/src/views/TagDetail/Followers/styles.css @@ -0,0 +1,51 @@ +.container { + @mixin flex-center-start; + + margin: var(--spacing-base) 0; +} + +.count { + @mixin flex-center-all; + + padding-left: var(--spacing-x-tight); + font-size: var(--font-size-sm); + color: var(--color-grey-darker); + + & b { + margin-right: var(--spacing-xx-tight); + color: var(--color-black); + } + + & span { + margin-top: 2px; + } +} + +.avatar-list { + @mixin flex-center-start; + + & :global(> *) { + position: relative; + box-shadow: 0 0 0 2px var(--color-white); + + &:nth-child(1) { + z-index: 5; + } + + &:nth-child(2) { + z-index: 4; + } + + &:nth-child(3) { + z-index: 3; + } + + &:nth-child(4) { + z-index: 2; + } + + &:nth-child(5) { + z-index: 1; + } + } +} diff --git a/src/views/TagDetail/TagDetailButtons/AddArticleButton/index.tsx b/src/views/TagDetail/TagDetailButtons/AddArticleButton/index.tsx deleted file mode 100644 index 816e36c305..0000000000 --- a/src/views/TagDetail/TagDetailButtons/AddArticleButton/index.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { Button, IconAdd, TextIcon, Translate } from '~/components' - -import TagArticleDialog from './TagArticleDialog' - -interface AddArticleButtonProps { - id?: string -} - -const AddArticleButton: React.FC = ({ id }) => { - return ( - - {({ open }) => ( - - )} - - ) -} - -export default AddArticleButton diff --git a/src/views/TagDetail/TagDetailButtons/EditTagButton/index.tsx b/src/views/TagDetail/TagDetailButtons/EditTagButton/index.tsx deleted file mode 100644 index 9d16a7c2ad..0000000000 --- a/src/views/TagDetail/TagDetailButtons/EditTagButton/index.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { Button, IconEdit, TagDialog, TextIcon, Translate } from '~/components' - -interface EditTagButtonProps { - id?: string - content?: string - description?: string -} - -const EditTagButton = (props: EditTagButtonProps) => ( - - {({ open }) => ( - - )} - -) - -export default EditTagButton diff --git a/src/views/TagDetail/TagDetailButtons/index.tsx b/src/views/TagDetail/TagDetailButtons/index.tsx deleted file mode 100644 index bb4dfe3432..0000000000 --- a/src/views/TagDetail/TagDetailButtons/index.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import AddArticleButton from './AddArticleButton' -import EditTagButton from './EditTagButton' - -export const TagDetailButtons = { - AddArticleButton, - EditTagButton, -} diff --git a/src/views/TagDetail/index.tsx b/src/views/TagDetail/index.tsx index 80dda324a0..e86933c1ec 100644 --- a/src/views/TagDetail/index.tsx +++ b/src/views/TagDetail/index.tsx @@ -8,6 +8,7 @@ import { useContext, useState } from 'react' import { EmptyTag, + Expandable, Head, Layout, PullToRefresh, @@ -15,6 +16,7 @@ import { Spinner, Tabs, Throw404, + Title, Translate, ViewerContext, } from '~/components' @@ -24,9 +26,12 @@ import { UserDigest } from '~/components/UserDigest' import { ERROR_CODES } from '~/common/enums' import { getQuery } from '~/common/utils' +import { TagDetailArticles } from './Articles' +import ArticlesCount from './ArticlesCount' +import { TagDetailButtons } from './Buttons' +import DropdownActions from './DropdownActions' +import Followers from './Followers' import styles from './styles.css' -import { TagDetailArticles } from './TagDetailArticles' -import { TagDetailButtons } from './TagDetailButtons' import { TagDetail as TagDetailType } from './__generated__/TagDetail' @@ -48,10 +53,12 @@ const TAG_DETAIL = gql` articles(input: { first: 0, selected: true }) { totalCount } + ...FollowButtonTag } } } ${UserDigest.Mini.fragments.user} + ${TagDetailButtons.FollowButton.fragments.tag} ` type TagFeed = 'latest' | 'selected' @@ -92,21 +99,14 @@ const TagDetail = ({ data }: { data: TagDetailType }) => { left={} right={ <> - + - {canEdit && ( -
- - -
- )} + } /> @@ -116,23 +116,39 @@ const TagDetail = ({ data }: { data: TagDetailType }) => { - {maintainer && ( -
- - - - +
+ {maintainer && ( +
+ + + + +
+ )} + + #{data.node.content} + + {data.node.description && ( + +

{data.node.description}

+
+ )} + +
+ +
- )} - {data.node.description && ( -

{data.node.description}

- )} +
+ + {canEdit && } +
+
{hasSelected > 0 && ( diff --git a/src/views/TagDetail/styles.css b/src/views/TagDetail/styles.css index d0ec9a93a4..21db4bdc9c 100644 --- a/src/views/TagDetail/styles.css +++ b/src/views/TagDetail/styles.css @@ -1,13 +1,14 @@ -.buttons { - & :global(> * + *) { - margin-left: var(--spacing-base); - } +.info { + @mixin border-bottom-grey; + + padding: 0 var(--spacing-base) var(--spacing-loose); + margin-bottom: var(--spacing-x-tight); } .maintainer { @mixin flex-center-start; - padding: 0 var(--spacing-base) var(--spacing-base); + padding: 0 0 var(--spacing-base); & span { margin-left: var(--spacing-x-tight); @@ -17,8 +18,19 @@ } .description { - padding: 0 var(--spacing-base); - font-size: var(--font-size-sm); + padding-top: 1rem; + font-size: var(--font-size-md-s); + line-height: 1.33333333; color: var(--color-grey-darker); white-space: pre-wrap; } + +.statistics { + @mixin flex-center-space-between; +} + +.buttons { + @mixin flex-center-space-between; + + padding-top: 1rem; +} From 7996d2cb38d822bf59e7fdebb68f39ca8a4acbaa Mon Sep 17 00:00:00 2001 From: RoberTu Date: Wed, 8 Jul 2020 17:22:15 +0800 Subject: [PATCH 09/86] feat(edit-article): merge sidebar components into one --- .../DropdownActions/RemoveTagButton.tsx | 2 +- .../DropdownActions/SetTagSelectedButton.tsx | 2 +- .../SetTagUnselectedButton.tsx | 2 +- .../Collection/CollectionList.tsx | 64 ------------ src/views/ArticleDetail/Collection/index.tsx | 49 +++++++++- .../ArticleDetail/EditMode/Header/index.tsx | 15 ++- .../EditMode/Sidebar/EditCollection.tsx | 63 ------------ .../EditMode/Sidebar/EditTags.tsx | 62 ------------ .../ArticleDetail/EditMode/Sidebar/index.tsx | 97 +++++++++++++++++-- src/views/ArticleDetail/TagList/index.tsx | 2 +- src/views/ArticleDetail/index.tsx | 66 +++++++------ 11 files changed, 178 insertions(+), 246 deletions(-) delete mode 100644 src/views/ArticleDetail/Collection/CollectionList.tsx delete mode 100644 src/views/ArticleDetail/EditMode/Sidebar/EditCollection.tsx delete mode 100644 src/views/ArticleDetail/EditMode/Sidebar/EditTags.tsx diff --git a/src/components/ArticleDigest/DropdownActions/RemoveTagButton.tsx b/src/components/ArticleDigest/DropdownActions/RemoveTagButton.tsx index df04f3db02..cb5d431420 100644 --- a/src/components/ArticleDigest/DropdownActions/RemoveTagButton.tsx +++ b/src/components/ArticleDigest/DropdownActions/RemoveTagButton.tsx @@ -25,7 +25,7 @@ const fragments = { article: gql` fragment RemoveTagButtonArticle on Article { id - tags @connection(key: "tagsList") { + tags { id creator { id diff --git a/src/components/ArticleDigest/DropdownActions/SetTagSelectedButton.tsx b/src/components/ArticleDigest/DropdownActions/SetTagSelectedButton.tsx index 8d19e4c1ee..2f4efc57a2 100644 --- a/src/components/ArticleDigest/DropdownActions/SetTagSelectedButton.tsx +++ b/src/components/ArticleDigest/DropdownActions/SetTagSelectedButton.tsx @@ -25,7 +25,7 @@ const fragments = { article: gql` fragment SetTagSelectedButtonArticle on Article { id - tags @connection(key: "tagsList") { + tags { id creator { id diff --git a/src/components/ArticleDigest/DropdownActions/SetTagUnselectedButton.tsx b/src/components/ArticleDigest/DropdownActions/SetTagUnselectedButton.tsx index a0ec925e60..2f92fd1c28 100644 --- a/src/components/ArticleDigest/DropdownActions/SetTagUnselectedButton.tsx +++ b/src/components/ArticleDigest/DropdownActions/SetTagUnselectedButton.tsx @@ -31,7 +31,7 @@ const fragments = { article: gql` fragment SetTagUnselectedButtonArticle on Article { id - tags @connection(key: "tagsList") { + tags { id creator { id diff --git a/src/views/ArticleDetail/Collection/CollectionList.tsx b/src/views/ArticleDetail/Collection/CollectionList.tsx deleted file mode 100644 index aa23a77519..0000000000 --- a/src/views/ArticleDetail/Collection/CollectionList.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import { ApolloError } from 'apollo-client' - -import { - ArticleDigestSidebar, - List, - Spinner, - useResponsive, -} from '~/components' -import { QueryError } from '~/components/GQL' - -import { analytics } from '~/common/utils' - -import { CollectionList as CollectionListTypes } from './__generated__/CollectionList' - -interface CollectionListProps { - data?: CollectionListTypes - loading: boolean - error?: ApolloError | null -} - -const CollectionList: React.FC = ({ - data, - loading, - error, -}) => { - const isMediumUp = useResponsive('md-up') - const { edges, pageInfo } = data?.article?.collection || {} - - if (loading) { - return - } - - if (error) { - return - } - - if (!edges || !pageInfo) { - return null - } - - return ( - - {edges.map(({ node, cursor }, i) => ( - - - analytics.trackEvent('click_feed', { - type: 'collection', - styleType: 'small_cover', - contentType: 'article', - location: i, - }) - } - /> - - ))} - - ) -} - -export default CollectionList diff --git a/src/views/ArticleDetail/Collection/index.tsx b/src/views/ArticleDetail/Collection/index.tsx index 126cf4ce92..4272d9430f 100644 --- a/src/views/ArticleDetail/Collection/index.tsx +++ b/src/views/ArticleDetail/Collection/index.tsx @@ -2,12 +2,20 @@ import { useQuery } from '@apollo/react-hooks' import gql from 'graphql-tag' import _uniq from 'lodash/uniq' -import { Title, Translate, ViewMoreButton } from '~/components' +import { + ArticleDigestSidebar, + List, + Spinner, + Title, + Translate, + useResponsive, + ViewMoreButton, +} from '~/components' +import { QueryError } from '~/components/GQL' import articleFragments from '~/components/GQL/fragments/article' -import { mergeConnections } from '~/common/utils' +import { analytics, mergeConnections } from '~/common/utils' -import CollectionList from './CollectionList' import styles from './styles.css' import { ArticleDetailPublic_article } from '../__generated__/ArticleDetailPublic' @@ -27,12 +35,13 @@ const Collection: React.FC<{ article: ArticleDetailPublic_article collectionCount?: number }> = ({ article, collectionCount }) => { + const isMediumUp = useResponsive('md-up') const { data, loading, error, fetchMore } = useQuery( COLLECTION_LIST, { variables: { mediaHash: article.mediaHash, first: 3 } } ) const connectionPath = 'article.collection' - const { pageInfo } = data?.article?.collection || {} + const { edges, pageInfo } = data?.article?.collection || {} const loadAll = () => fetchMore({ variables: { @@ -48,6 +57,18 @@ const Collection: React.FC<{ }), }) + if (loading) { + return + } + + if (error) { + return + } + + if (!edges || !pageInfo) { + return null + } + return (
@@ -60,7 +81,25 @@ const Collection: React.FC<{
- + + {edges.map(({ node, cursor }, i) => ( + + + analytics.trackEvent('click_feed', { + type: 'collection', + styleType: 'small_cover', + contentType: 'article', + location: i, + }) + } + /> + + ))} + {pageInfo?.hasNextPage && } diff --git a/src/views/ArticleDetail/EditMode/Header/index.tsx b/src/views/ArticleDetail/EditMode/Header/index.tsx index 50192529ba..58affc8411 100644 --- a/src/views/ArticleDetail/EditMode/Header/index.tsx +++ b/src/views/ArticleDetail/EditMode/Header/index.tsx @@ -1,5 +1,4 @@ import gql from 'graphql-tag' -import _uniq from 'lodash/uniq' import { Button, IconSpinner, Tag, TextIcon, Translate } from '~/components' import { useMutation } from '~/components/GQL' @@ -15,8 +14,8 @@ import { EditArticle } from './__generated__/EditArticle' interface EditModeHeaderProps { id: string mediaHash: string - tags: string[] - collection: ArticleDigestDropdownArticle[] + editModeTags: string[] + editModeCollection: ArticleDigestDropdownArticle[] setEditMode: (enable: boolean) => any } @@ -37,7 +36,7 @@ const EDIT_ARTICLE = gql` ) { editArticle(input: { id: $id, tags: $tags, collection: $collection }) { id - tags @connection(key: "tagsList") { + tags { ...DigestTag selected(input: { mediaHash: $mediaHash }) } @@ -51,8 +50,8 @@ const EDIT_ARTICLE = gql` const EditModeHeader = ({ id, mediaHash, - tags, - collection, + editModeTags, + editModeCollection, setEditMode, }: EditModeHeaderProps) => { const [editArticle, { loading }] = useMutation(EDIT_ARTICLE) @@ -63,8 +62,8 @@ const EditModeHeader = ({ variables: { id, mediaHash, - tags, - collection: _uniq(collection.map(({ id: articleId }) => articleId)), + tags: editModeTags, + collection: editModeCollection.map(({ id: articleId }) => articleId), first: null, }, }) diff --git a/src/views/ArticleDetail/EditMode/Sidebar/EditCollection.tsx b/src/views/ArticleDetail/EditMode/Sidebar/EditCollection.tsx deleted file mode 100644 index 67c02683ff..0000000000 --- a/src/views/ArticleDetail/EditMode/Sidebar/EditCollection.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import { useQuery } from '@apollo/react-hooks' -import gql from 'graphql-tag' -import _uniq from 'lodash/uniq' -import { useEffect } from 'react' - -import { Spinner } from '~/components' -import SidebarCollection from '~/components/Editor/Sidebar/Collection' -import { QueryError } from '~/components/GQL' -import articleFragments from '~/components/GQL/fragments/article' - -import { ArticleDigestDropdownArticle } from '~/components/ArticleDigest/Dropdown/__generated__/ArticleDigestDropdownArticle' -import { ArticleCollection } from './__generated__/ArticleCollection' - -export interface EditCollectionProps { - articleId: string - collection: ArticleDigestDropdownArticle[] - setCollection: (articles: ArticleDigestDropdownArticle[]) => any -} - -const ARTICLE_COLLECTION = gql` - query ArticleCollection($id: ID!, $after: String, $first: Int = null) { - node(input: { id: $id }) { - id - ... on Article { - ...ArticleCollection - } - } - } - ${articleFragments.articleCollection} -` - -const EditCollection = ({ - articleId, - collection, - setCollection, -}: EditCollectionProps) => { - const { data, loading, error } = useQuery( - ARTICLE_COLLECTION, - { - variables: { id: articleId }, - } - ) - - useEffect(() => { - const articles = - (data?.node?.__typename === 'Article' && - data.node.collection.edges?.map(({ node }) => node)) || - [] - setCollection(articles) - }, [data?.node?.__typename]) - - if (loading) { - return - } - - if (error) { - return - } - - return -} - -export default EditCollection diff --git a/src/views/ArticleDetail/EditMode/Sidebar/EditTags.tsx b/src/views/ArticleDetail/EditMode/Sidebar/EditTags.tsx deleted file mode 100644 index 22055cba84..0000000000 --- a/src/views/ArticleDetail/EditMode/Sidebar/EditTags.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { useQuery } from '@apollo/react-hooks' -import gql from 'graphql-tag' -import _uniq from 'lodash/uniq' -import { useEffect } from 'react' - -import { Spinner, Tag } from '~/components' -import SidebarTags from '~/components/Editor/Sidebar/Tags' -import { QueryError } from '~/components/GQL' - -import { ArticleTags } from './__generated__/ArticleTags' - -export interface EditTagsProps { - articleId: string - tags: string[] - setTags: (tags: string[]) => any -} - -const ARTICLE_TAGS = gql` - query ArticleTags($id: ID!) { - node(input: { id: $id }) { - id - ... on Article { - tags @connection(key: "tagsList") { - ...DigestTag - } - } - } - } - ${Tag.fragments.tag} -` - -const EditTags = ({ articleId, tags, setTags }: EditTagsProps) => { - const { data, loading, error } = useQuery(ARTICLE_TAGS, { - variables: { id: articleId }, - }) - - useEffect(() => { - setTags( - (data?.node?.__typename === 'Article' && - data.node.tags?.map(({ content }) => content)) || - [] - ) - }, [data?.node?.__typename]) - - if (loading) { - return - } - - if (error) { - return - } - - return ( - setTags(_uniq(tags.concat(tag)))} - onDeleteTag={(tag) => setTags(tags.filter((it) => it !== tag))} - /> - ) -} - -export default EditTags diff --git a/src/views/ArticleDetail/EditMode/Sidebar/index.tsx b/src/views/ArticleDetail/EditMode/Sidebar/index.tsx index 13207335a7..5fee85f315 100644 --- a/src/views/ArticleDetail/EditMode/Sidebar/index.tsx +++ b/src/views/ArticleDetail/EditMode/Sidebar/index.tsx @@ -1,13 +1,92 @@ -import EditCollection, { EditCollectionProps } from './EditCollection' -import EditTags, { EditTagsProps } from './EditTags' +import { useQuery } from '@apollo/react-hooks' +import gql from 'graphql-tag' +import _uniq from 'lodash/uniq' +import { useEffect } from 'react' -type EditModeSidebarProps = EditTagsProps & EditCollectionProps +import { Spinner, Tag } from '~/components' +import SidebarCollection from '~/components/Editor/Sidebar/Collection' +import SidebarTags from '~/components/Editor/Sidebar/Tags' +import { QueryError } from '~/components/GQL' +import articleFragments from '~/components/GQL/fragments/article' -const EditModeSidebar = (props: EditModeSidebarProps) => ( - <> - - - -) +import { ArticleDigestDropdownArticle } from '~/components/ArticleDigest/Dropdown/__generated__/ArticleDigestDropdownArticle' +import { EditModeArticle } from './__generated__/EditModeArticle' + +interface EditModeSidebarProps { + mediaHash: string + editModeTags: string[] + setEditModeTags: (tags: string[]) => any + editModeCollection: ArticleDigestDropdownArticle[] + setEditModeCollection: (articles: ArticleDigestDropdownArticle[]) => any +} + +const EDIT_MODE_ARTICLE = gql` + query EditModeArticle( + $mediaHash: String! + $after: String + $first: Int = null + ) { + article(input: { mediaHash: $mediaHash }) { + id + tags { + ...DigestTag + } + ...ArticleCollection + } + } + ${Tag.fragments.tag} + ${articleFragments.articleCollection} +` + +const EditModeSidebar = ({ + mediaHash, + editModeTags, + setEditModeTags, + editModeCollection, + setEditModeCollection, +}: EditModeSidebarProps) => { + const { data, loading, error } = useQuery( + EDIT_MODE_ARTICLE, + { + variables: { mediaHash }, + } + ) + const article = data?.article + const tags = article?.tags?.map(({ content }) => content) || [] + const collection = article?.collection.edges?.map(({ node }) => node) || [] + + useEffect(() => { + setEditModeTags(tags) + setEditModeCollection(collection) + }, [data?.article?.id]) + + if (loading) { + return + } + + if (error) { + return + } + + if (!article) { + return null + } + + return ( + <> + 0 ? editModeTags : tags} + onAddTag={(tag) => setEditModeTags(_uniq(tags.concat(tag)))} + onDeleteTag={(tag) => setEditModeTags(tags.filter((it) => it !== tag))} + /> + 0 ? editModeCollection : collection + } + onEdit={setEditModeCollection} + /> + + ) +} export default EditModeSidebar diff --git a/src/views/ArticleDetail/TagList/index.tsx b/src/views/ArticleDetail/TagList/index.tsx index 921be23d1d..67b79bc444 100644 --- a/src/views/ArticleDetail/TagList/index.tsx +++ b/src/views/ArticleDetail/TagList/index.tsx @@ -9,7 +9,7 @@ import { TagListArticle } from './__generated__/TagListArticle' const fragments = { article: gql` fragment TagListArticle on Article { - tags @connection(key: "tagsList") { + tags { ...DigestTag selected(input: { mediaHash: $mediaHash }) } diff --git a/src/views/ArticleDetail/index.tsx b/src/views/ArticleDetail/index.tsx index 0fcfc3dc01..be17b08adc 100644 --- a/src/views/ArticleDetail/index.tsx +++ b/src/views/ArticleDetail/index.tsx @@ -159,15 +159,15 @@ const ArticleDetail = () => { ) } - /** - * Render - */ useEffect(() => { if (shouldShowWall && window.location.hash && article) { jump('#comments', { offset: -10 }) } }, [mediaHash]) + /** + * Render:Loading + */ if (loading) { return ( @@ -176,6 +176,9 @@ const ArticleDetail = () => { ) } + /** + * Render:Error + */ if (error) { return ( @@ -184,6 +187,9 @@ const ArticleDetail = () => { ) } + /** + * Render:404 + */ if (!article) { return ( @@ -193,7 +199,7 @@ const ArticleDetail = () => { } /** - * Archived or Banned + * Render:Archived/Banned */ if (article.state !== 'active' && viewer.id !== authorId) { return ( @@ -221,54 +227,52 @@ const ArticleDetail = () => { } /** - * Edit Mode + * Render:Edit Mode */ - const sidebarProps = { - articleId: article.id, - tags: editModeTags, - collection: editModeCollection, - setTags: setEditModeTags, - setCollection: setEditModeCollection, - } if (editMode) { return ( - }> + + } + keepAside + > } /> - {isLargeUp && ( -
-
- - {translate && titleTranslation - ? titleTranslation - : article.title} - -
- +
+
+ + {translate && titleTranslation ? titleTranslation : article.title} +
- )} - {!isLargeUp && ( - - - - )} + +
) } + /** + * Render + */ return ( }> Date: Wed, 8 Jul 2020 20:55:43 +0800 Subject: [PATCH 10/86] feat(edit-article): referch article on edit saved --- .../ArticleDetail/EditMode/Header/index.tsx | 6 ++-- src/views/ArticleDetail/index.tsx | 31 ++++++++++--------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/views/ArticleDetail/EditMode/Header/index.tsx b/src/views/ArticleDetail/EditMode/Header/index.tsx index 58affc8411..1142b36758 100644 --- a/src/views/ArticleDetail/EditMode/Header/index.tsx +++ b/src/views/ArticleDetail/EditMode/Header/index.tsx @@ -16,7 +16,7 @@ interface EditModeHeaderProps { mediaHash: string editModeTags: string[] editModeCollection: ArticleDigestDropdownArticle[] - setEditMode: (enable: boolean) => any + onEditSaved: () => any } /** @@ -52,7 +52,7 @@ const EditModeHeader = ({ mediaHash, editModeTags, editModeCollection, - setEditMode, + onEditSaved, }: EditModeHeaderProps) => { const [editArticle, { loading }] = useMutation(EDIT_ARTICLE) @@ -67,7 +67,7 @@ const EditModeHeader = ({ first: null, }, }) - setEditMode(false) + onEditSaved() } catch (e) { window.dispatchEvent( new CustomEvent(ADD_TOAST, { diff --git a/src/views/ArticleDetail/index.tsx b/src/views/ArticleDetail/index.tsx index be17b08adc..8d1e619ffd 100644 --- a/src/views/ArticleDetail/index.tsx +++ b/src/views/ArticleDetail/index.tsx @@ -75,13 +75,6 @@ const ArticleDetail = () => { const [fixedWall, setFixedWall] = useState(false) // const [showResponses, setShowResponses] = useState(false) - // edit mode - const [editMode, setEditMode] = useState(false) - const [editModeTags, setEditModeTags] = useState([]) - const [editModeCollection, setEditModeCollection] = useState< - ArticleDigestDropdownArticle[] - >([]) - // wall const { data: clientPreferenceData } = useQuery( CLIENT_PREFERENCE, @@ -93,12 +86,11 @@ const ArticleDetail = () => { const shouldShowWall = !viewer.isAuthed && wall // public data - const { data, loading, error, client } = useQuery( - ARTICLE_DETAIL_PUBLIC, - { - variables: { mediaHash }, - } - ) + const { data, loading, error, client, refetch: refetchPublic } = useQuery< + ArticleDetailPublic + >(ARTICLE_DETAIL_PUBLIC, { + variables: { mediaHash }, + }) const article = data?.article const authorId = article?.author?.id @@ -159,6 +151,17 @@ const ArticleDetail = () => { ) } + // edit mode + const [editMode, setEditMode] = useState(false) + const [editModeTags, setEditModeTags] = useState([]) + const [editModeCollection, setEditModeCollection] = useState< + ArticleDigestDropdownArticle[] + >([]) + const onEditSaved = () => { + setEditMode(false) + refetchPublic() + } + useEffect(() => { if (shouldShowWall && window.location.hash && article) { jump('#comments', { offset: -10 }) @@ -250,7 +253,7 @@ const ArticleDetail = () => { mediaHash={mediaHash} editModeTags={editModeTags} editModeCollection={editModeCollection} - setEditMode={setEditMode} + onEditSaved={onEditSaved} /> } /> From 77a0f8cd8a0d8710bd11779925ab9c0bc64ad512 Mon Sep 17 00:00:00 2001 From: RoberTu Date: Wed, 8 Jul 2020 21:15:10 +0800 Subject: [PATCH 11/86] feat(edit-article): deprecating `archiveArticle` & `updateArticleInfo` mutations use `editArticle` instead --- .../DropdownActions/ArchiveArticle/Dialog.tsx | 4 ++-- .../ArticleDigest/DropdownActions/StickyButton.tsx | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/components/ArticleDigest/DropdownActions/ArchiveArticle/Dialog.tsx b/src/components/ArticleDigest/DropdownActions/ArchiveArticle/Dialog.tsx index df64b16e7e..27c51a9ef9 100644 --- a/src/components/ArticleDigest/DropdownActions/ArchiveArticle/Dialog.tsx +++ b/src/components/ArticleDigest/DropdownActions/ArchiveArticle/Dialog.tsx @@ -12,7 +12,7 @@ import { ArchiveArticleArticle } from './__generated__/ArchiveArticleArticle' const ARCHIVE_ARTICLE = gql` mutation ArchiveArticle($id: ID!) { - archiveArticle(input: { id: $id }) { + editArticle(input: { id: $id, state: archived }) { id articleState: state sticky @@ -36,7 +36,7 @@ const ArchiveArticleDialog = ({ const [archiveArticle] = useMutation(ARCHIVE_ARTICLE, { variables: { id: article.id }, optimisticResponse: { - archiveArticle: { + editArticle: { id: article.id, articleState: 'archived' as any, sticky: false, diff --git a/src/components/ArticleDigest/DropdownActions/StickyButton.tsx b/src/components/ArticleDigest/DropdownActions/StickyButton.tsx index 261f7df137..aa0fa6eb7f 100644 --- a/src/components/ArticleDigest/DropdownActions/StickyButton.tsx +++ b/src/components/ArticleDigest/DropdownActions/StickyButton.tsx @@ -11,11 +11,11 @@ import { useMutation } from '~/components/GQL' import updateUserArticles from '~/components/GQL/updates/userArticles' import { StickyButtonArticle } from './__generated__/StickyButtonArticle' -import { UpdateArticleInfo } from './__generated__/UpdateArticleInfo' +import { ToggleSticky } from './__generated__/ToggleSticky' -const UPDATE_ARTICLE_INFO = gql` - mutation UpdateArticleInfo($id: ID!, $sticky: Boolean!) { - updateArticleInfo(input: { id: $id, sticky: $sticky }) { +const TOGGLE_STICKY = gql` + mutation ToggleSticky($id: ID!, $sticky: Boolean!) { + editArticle(input: { id: $id, sticky: $sticky }) { id sticky } @@ -36,10 +36,10 @@ const fragments = { } const StickyButton = ({ article }: { article: StickyButtonArticle }) => { - const [update] = useMutation(UPDATE_ARTICLE_INFO, { + const [toggleSticky] = useMutation(TOGGLE_STICKY, { variables: { id: article.id, sticky: !article.sticky }, optimisticResponse: { - updateArticleInfo: { + editArticle: { id: article.id, sticky: !article.sticky, __typename: 'Article', @@ -58,7 +58,7 @@ const StickyButton = ({ article }: { article: StickyButtonArticle }) => { return ( { - update() + toggleSticky() }} > {article.sticky ? ( From 56a409223e0ea98bc2602a0fae8f54a0fc904eb1 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 8 Jul 2020 22:51:30 +0000 Subject: [PATCH 12/86] build(deps): bump lodash from 4.17.15 to 4.17.19 Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.19. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.19) Signed-off-by: dependabot-preview[bot] --- package-lock.json | 14 ++++++++++---- package.json | 2 +- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 96e4d99d0d..499488fb7f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "matters-web", - "version": "3.9.0", + "version": "3.10.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -9309,6 +9309,12 @@ "path-is-absolute": "^1.0.0" } }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + }, "strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", @@ -17231,9 +17237,9 @@ } }, "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", + "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==" }, "lodash-es": { "version": "4.17.15", diff --git a/package.json b/package.json index 003afd6a83..5a10e0ca5b 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "helmet": "^3.23.3", "isomorphic-unfetch": "^3.0.0", "jump.js": "^1.0.2", - "lodash": "^4.17.15", + "lodash": "^4.17.19", "next": "^9.4.4", "next-with-apollo": "^5.1.0", "nprogress": "^0.2.0", From 04d9e96286aca72c8a8c4b80aa68309bbddf8855 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 8 Jul 2020 22:53:54 +0000 Subject: [PATCH 13/86] build(deps-dev): bump @testing-library/react from 10.4.4 to 10.4.5 Bumps [@testing-library/react](https://github.com/testing-library/react-testing-library) from 10.4.4 to 10.4.5. - [Release notes](https://github.com/testing-library/react-testing-library/releases) - [Changelog](https://github.com/testing-library/react-testing-library/blob/master/CHANGELOG.md) - [Commits](https://github.com/testing-library/react-testing-library/compare/v10.4.4...v10.4.5) Signed-off-by: dependabot-preview[bot] --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 96e4d99d0d..685b089760 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "matters-web", - "version": "3.9.0", + "version": "3.10.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -5648,9 +5648,9 @@ } }, "@testing-library/react": { - "version": "10.4.4", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-10.4.4.tgz", - "integrity": "sha512-SKDQ2jBdg9UQQYQragkvXOzNp4hnCdOvXyZ52rg+OXiiumVxkAutdvvRzBF4PrbvMQ27Z6gx0GVo2YQ1Mcip8g==", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-10.4.5.tgz", + "integrity": "sha512-M5A0W4VphBiEm4vgnq7vHC+/e4Bp/3iIOAWap1FtIiA+Zom6BtXpY3RSTOqc8bZsCcu9gFBZ/lxaiMW6uJddWg==", "dev": true, "requires": { "@babel/runtime": "^7.10.3", diff --git a/package.json b/package.json index 003afd6a83..369de0c34c 100644 --- a/package.json +++ b/package.json @@ -89,7 +89,7 @@ "devDependencies": { "@babel/plugin-proposal-optional-chaining": "^7.10.4", "@svgr/webpack": "^5.4.0", - "@testing-library/react": "^10.4.4", + "@testing-library/react": "^10.4.5", "@types/autosize": "^3.0.7", "@types/classnames": "^2.2.10", "@types/express": "^4.17.4", From 718717b2f546056f6f5eebad30e2edf9af6a633c Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 9 Jul 2020 02:14:50 +0000 Subject: [PATCH 14/86] build(deps-dev): bump @types/express from 4.17.6 to 4.17.7 Bumps [@types/express](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/express) from 4.17.6 to 4.17.7. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/express) Signed-off-by: dependabot-preview[bot] --- package-lock.json | 12 ++++++------ package.json | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 685b089760..5c1a42f682 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5798,9 +5798,9 @@ "dev": true }, "@types/express": { - "version": "4.17.6", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.6.tgz", - "integrity": "sha512-n/mr9tZI83kd4azlPG5y997C/M4DNABK9yErhFM6hKdym4kkmd9j0vtsJyjFIwfRBxtrxZtAfGZCNRIBMFLK5w==", + "version": "4.17.7", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.7.tgz", + "integrity": "sha512-dCOT5lcmV/uC2J9k0rPafATeeyz+99xTt54ReX11/LObZgfzJqZNcW27zGhYyX+9iSEGXGt5qLPwRSvBZcLvtQ==", "dev": true, "requires": { "@types/body-parser": "*", @@ -5810,9 +5810,9 @@ } }, "@types/express-serve-static-core": { - "version": "4.17.7", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.7.tgz", - "integrity": "sha512-EMgTj/DF9qpgLXyc+Btimg+XoH7A2liE8uKul8qSmMTHCeNYzydDKFdsJskDvw42UsesCnhO63dO0Grbj8J4Dw==", + "version": "4.17.8", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.8.tgz", + "integrity": "sha512-1SJZ+R3Q/7mLkOD9ewCBDYD2k0WyZQtWYqF/2VvoNN2/uhI49J9CDN4OAm+wGMA0DbArA4ef27xl4+JwMtGggw==", "dev": true, "requires": { "@types/node": "*", diff --git a/package.json b/package.json index 369de0c34c..ce797dd183 100644 --- a/package.json +++ b/package.json @@ -92,7 +92,7 @@ "@testing-library/react": "^10.4.5", "@types/autosize": "^3.0.7", "@types/classnames": "^2.2.10", - "@types/express": "^4.17.4", + "@types/express": "^4.17.7", "@types/fingerprintjs2": "^2.0.0", "@types/grecaptcha": "^3.0.1", "@types/helmet": "0.0.47", From 611c061a9e832199033c412656b4181628843141 Mon Sep 17 00:00:00 2001 From: Zeck Li Date: Thu, 9 Jul 2020 15:00:44 +0800 Subject: [PATCH 15/86] feat(component): refactor tag detail page and add tag feed in follow page --- src/common/enums/errorCode.ts | 1 + src/common/enums/text.ts | 2 + src/common/utils/types/index.ts | 1 + .../DropdownActions/SetTagSelectedButton.tsx | 4 +- .../SetTagUnselectedButton.tsx | 4 +- src/components/Buttons/Write/index.tsx | 13 +- src/components/GQL/mutations/createDraft.ts | 10 + .../GQL/updates/tagArticlesCount.ts | 12 +- .../Follow/FollowFeed/ArticlesFeed/index.tsx | 111 ++++++++++ .../Follow/FollowFeed/CommentsFeed/index.tsx | 106 +++++++++ .../Follow/FollowFeed/FeedType/index.tsx | 7 +- .../Follow/FollowFeed/TagsFeed/index.tsx | 194 ++++++++++++++++ .../Follow/FollowFeed/TagsFeed/styles.css | 4 + src/views/Follow/FollowFeed/index.tsx | 208 +----------------- .../AddButton/TagArticleDialog/Content.tsx | 36 ++- .../AddButton/TagArticleDialog/index.tsx | 8 +- .../TagDetail/Buttons/AddButton/index.tsx | 137 ++++++++++-- src/views/TagDetail/DropdownActions/index.tsx | 6 +- src/views/TagDetail/index.tsx | 23 +- src/views/TagDetail/styles.css | 2 +- 20 files changed, 625 insertions(+), 264 deletions(-) create mode 100644 src/components/GQL/mutations/createDraft.ts create mode 100644 src/views/Follow/FollowFeed/ArticlesFeed/index.tsx create mode 100644 src/views/Follow/FollowFeed/CommentsFeed/index.tsx create mode 100644 src/views/Follow/FollowFeed/TagsFeed/index.tsx create mode 100644 src/views/Follow/FollowFeed/TagsFeed/styles.css diff --git a/src/common/enums/errorCode.ts b/src/common/enums/errorCode.ts index 2d87f87d42..44f02cd55b 100644 --- a/src/common/enums/errorCode.ts +++ b/src/common/enums/errorCode.ts @@ -43,6 +43,7 @@ export const ERROR_CODES = { // TAG DUPLICATE_TAG: 'DUPLICATE_TAG', + NOT_ALLOW_ADD_TAG: 'NOT_ALLOW_ADD_TAG', // Verification Code CODE_INVALID: 'CODE_INVALID', diff --git a/src/common/enums/text.ts b/src/common/enums/text.ts index 24a111614d..a79508afc1 100644 --- a/src/common/enums/text.ts +++ b/src/common/enums/text.ts @@ -152,6 +152,7 @@ export const TEXT = { NETWORK_ERROR: '網路錯誤,請用力刷新', newPassword: '新密碼', nextStep: '下一步', + NOT_ALLOW_ADD_TAG: '無法添加', NOT_ENOUGH_MAT: '沒有足夠的 MAT 以讚賞', NOTICE_NOT_FOUND: '通知不存在', notification: '通知', @@ -425,6 +426,7 @@ export const TEXT = { NETWORK_ERROR: '网络不给力,请用力刷新', newPassword: '新密码', nextStep: '下一步', + NOT_ALLOW_ADD_TAG: '无法添加', NOT_ENOUGH_MAT: '没有足够的 MAT 以赞赏', NOTICE_NOT_FOUND: '通知不存在', notification: '通知', diff --git a/src/common/utils/types/index.ts b/src/common/utils/types/index.ts index 60d9260b86..99d7ec3ca2 100644 --- a/src/common/utils/types/index.ts +++ b/src/common/utils/types/index.ts @@ -54,6 +54,7 @@ export default gql` enum FollowFeedType { article comment + tag } enum ViewMode { diff --git a/src/components/ArticleDigest/DropdownActions/SetTagSelectedButton.tsx b/src/components/ArticleDigest/DropdownActions/SetTagSelectedButton.tsx index 6323c1fb43..a3459c74d3 100644 --- a/src/components/ArticleDigest/DropdownActions/SetTagSelectedButton.tsx +++ b/src/components/ArticleDigest/DropdownActions/SetTagSelectedButton.tsx @@ -12,7 +12,9 @@ import { SetTagSelectedButtonArticle } from './__generated__/SetTagSelectedButto const SET_TAG_SELECTED = gql` mutation SetTagSelected($id: ID!, $articles: [ID!]) { - putArticlesTags(input: { id: $id, articles: $articles, selected: true }) { + updateArticlesTags( + input: { id: $id, articles: $articles, isSelected: true } + ) { id articles(input: { first: 0, selected: true }) { totalCount diff --git a/src/components/ArticleDigest/DropdownActions/SetTagUnselectedButton.tsx b/src/components/ArticleDigest/DropdownActions/SetTagUnselectedButton.tsx index 2f92fd1c28..e29534ac02 100644 --- a/src/components/ArticleDigest/DropdownActions/SetTagUnselectedButton.tsx +++ b/src/components/ArticleDigest/DropdownActions/SetTagUnselectedButton.tsx @@ -18,7 +18,9 @@ import { SetTagUnselectedButtonArticle } from './__generated__/SetTagUnselectedB const SET_TAG_UNSELECTED = gql` mutation SetTagUnselected($id: ID!, $articles: [ID!]) { - putArticlesTags(input: { id: $id, articles: $articles, selected: false }) { + updateArticlesTags( + input: { id: $id, articles: $articles, isSelected: false } + ) { id articles(input: { first: 0, selected: true }) { totalCount diff --git a/src/components/Buttons/Write/index.tsx b/src/components/Buttons/Write/index.tsx index b1d6275c5a..8034eb3759 100644 --- a/src/components/Buttons/Write/index.tsx +++ b/src/components/Buttons/Write/index.tsx @@ -1,4 +1,3 @@ -import gql from 'graphql-tag' import { useContext } from 'react' import { @@ -11,6 +10,7 @@ import { Translate, } from '~/components' import { useMutation } from '~/components/GQL' +import CREATE_DRAFT from '~/components/GQL/mutations/createDraft' import { ADD_TOAST, TEXT } from '~/common/enums' import { @@ -21,7 +21,7 @@ import { translate, } from '~/common/utils' -import { CreateDraft } from './__generated__/CreateDraft' +import { CreateDraft } from '~/components/GQL/mutations/__generated__/CreateDraft' interface Props { allowed: boolean @@ -29,15 +29,6 @@ interface Props { forbidden?: boolean } -export const CREATE_DRAFT = gql` - mutation CreateDraft($title: String!) { - putDraft(input: { title: $title }) { - id - slug - } - } -` - const BaseWriteButton = ({ onClick, loading, diff --git a/src/components/GQL/mutations/createDraft.ts b/src/components/GQL/mutations/createDraft.ts new file mode 100644 index 0000000000..645f23b551 --- /dev/null +++ b/src/components/GQL/mutations/createDraft.ts @@ -0,0 +1,10 @@ +import gql from 'graphql-tag' + +export default gql` + mutation CreateDraft($title: String!, $tags: [String!]) { + putDraft(input: { title: $title, tags: $tags }) { + id + slug + } + } +` diff --git a/src/components/GQL/updates/tagArticlesCount.ts b/src/components/GQL/updates/tagArticlesCount.ts index a134537889..c26044d1b9 100644 --- a/src/components/GQL/updates/tagArticlesCount.ts +++ b/src/components/GQL/updates/tagArticlesCount.ts @@ -1,4 +1,5 @@ import { DataProxy } from 'apollo-cache' +import _cloneDeep from 'lodash/cloneDeep' import TAG_ARTICLES_COUNT from '~/components/GQL/queries/tagArticlesCount' @@ -9,10 +10,12 @@ import { TagArticlesCount } from '~/components/GQL/queries/__generated__/TagArti const update = ({ cache, id, + count = 1, type, }: { cache: DataProxy id: string + count?: number type: 'increment' | 'decrement' }) => { try { @@ -26,20 +29,21 @@ const update = ({ variables, }) - if (!cacheData || !cacheData.node || cacheData.node.__typename !== 'Tag') { + const data = _cloneDeep(cacheData) + if (!data || !data.node || data.node.__typename !== 'Tag') { return } if (type === 'increment') { - cacheData.node.articles.totalCount++ + data.node.articles.totalCount += count } else { - cacheData.node.articles.totalCount-- + data.node.articles.totalCount -= count } cache.writeQuery({ query: TAG_ARTICLES_COUNT, variables, - data: cacheData, + data, }) } catch (e) { if (e.message.startsWith("Can't find field")) { diff --git a/src/views/Follow/FollowFeed/ArticlesFeed/index.tsx b/src/views/Follow/FollowFeed/ArticlesFeed/index.tsx new file mode 100644 index 0000000000..9ee5bc2859 --- /dev/null +++ b/src/views/Follow/FollowFeed/ArticlesFeed/index.tsx @@ -0,0 +1,111 @@ +import { useQuery } from '@apollo/react-hooks' +import gql from 'graphql-tag' + +import { + ArticleDigestFeed, + EmptyArticle, + InfiniteScroll, + List, + Spinner, +} from '~/components' +import { QueryError } from '~/components/GQL' + +import { analytics, mergeConnections } from '~/common/utils' + +import { FollowArticlesFeed } from './__generated__/FollowArticlesFeed' + +const FOLLOW_ARTICLES = gql` + query FollowArticlesFeed($after: String) { + viewer { + id + recommendation { + followeeArticles(input: { first: 10, after: $after }) { + pageInfo { + startCursor + endCursor + hasNextPage + } + edges { + cursor + node { + __typename + ... on Article { + ...ArticleDigestFeedArticle + } + } + } + } + } + } + } + ${ArticleDigestFeed.fragments.article} +` + +const ArticlesFeed = () => { + const { data, loading, error, fetchMore, refetch } = useQuery< + FollowArticlesFeed + >(FOLLOW_ARTICLES) + + if (loading) { + return + } + + if (error) { + return + } + + const connectionPath = 'viewer.recommendation.followeeArticles' + const { edges, pageInfo } = + data?.viewer?.recommendation.followeeArticles || {} + + if (!edges || edges.length <= 0 || !pageInfo) { + return + } + + const loadMore = () => { + analytics.trackEvent('load_more', { + type: 'follow', + location: edges.length, + }) + return fetchMore({ + variables: { + after: pageInfo.endCursor, + }, + updateQuery: (previousResult, { fetchMoreResult }) => + mergeConnections({ + oldData: previousResult, + newData: fetchMoreResult, + path: connectionPath, + }), + }) + } + + return ( + + + {edges.map(({ node, cursor }, i) => ( + + + analytics.trackEvent('click_feed', { + type: 'follow-article', + contentType: 'article', + styleType: 'no_cover', + location: i, + }) + } + inFollowFeed + /> + + ))} + + + ) +} + +export default ArticlesFeed diff --git a/src/views/Follow/FollowFeed/CommentsFeed/index.tsx b/src/views/Follow/FollowFeed/CommentsFeed/index.tsx new file mode 100644 index 0000000000..0921aa0062 --- /dev/null +++ b/src/views/Follow/FollowFeed/CommentsFeed/index.tsx @@ -0,0 +1,106 @@ +import { useQuery } from '@apollo/react-hooks' +import gql from 'graphql-tag' + +import { EmptyArticle, InfiniteScroll, List, Spinner } from '~/components' +import { QueryError } from '~/components/GQL' + +import { analytics, mergeConnections } from '~/common/utils' + +import FollowComment from '../FollowComment' + +import { FollowCommentsFeed } from './__generated__/FollowCommentsFeed' + +const FOLLOW_COMMENTS = gql` + query FollowCommentsFeed($after: String) { + viewer { + id + recommendation { + followeeComments(input: { first: 10, after: $after }) { + pageInfo { + startCursor + endCursor + hasNextPage + } + edges { + cursor + node { + __typename + ... on Comment { + ...FollowComment + } + } + } + } + } + } + } + ${FollowComment.fragments.comment} +` + +const CommentsFeed = () => { + const { data, loading, error, fetchMore, refetch } = useQuery< + FollowCommentsFeed + >(FOLLOW_COMMENTS) + + if (loading) { + return + } + + if (error) { + return + } + + const connectionPath = 'viewer.recommendation.followeeComments' + const { edges, pageInfo } = + data?.viewer?.recommendation.followeeComments || {} + + if (!edges || edges.length <= 0 || !pageInfo) { + return + } + + const loadMore = () => { + analytics.trackEvent('load_more', { + type: 'follow', + location: edges.length, + }) + return fetchMore({ + variables: { + after: pageInfo.endCursor, + }, + updateQuery: (previousResult, { fetchMoreResult }) => + mergeConnections({ + oldData: previousResult, + newData: fetchMoreResult, + path: connectionPath, + }), + }) + } + + return ( + + + {edges.map(({ node, cursor }, i) => ( + + + analytics.trackEvent('click_feed', { + type: 'follow-comment', + contentType: 'comment', + styleType: 'card', + location: i, + }) + } + /> + + ))} + + + ) +} + +export default CommentsFeed diff --git a/src/views/Follow/FollowFeed/FeedType/index.tsx b/src/views/Follow/FollowFeed/FeedType/index.tsx index cdefea6164..74a5c32ecd 100644 --- a/src/views/Follow/FollowFeed/FeedType/index.tsx +++ b/src/views/Follow/FollowFeed/FeedType/index.tsx @@ -1,6 +1,6 @@ import { Tabs, Translate } from '~/components' -export type FollowFeedType = 'article' | 'comment' +export type FollowFeedType = 'article' | 'comment' | 'tag' interface FeedTypeProps { type: FollowFeedType @@ -10,6 +10,7 @@ interface FeedTypeProps { const FeedType: React.FC = ({ type, setFeedType }) => { const isArticle = type === 'article' const isComment = type === 'comment' + const isTag = type === 'tag' return ( @@ -20,6 +21,10 @@ const FeedType: React.FC = ({ type, setFeedType }) => { setFeedType('comment')} selected={isComment}> + + setFeedType('tag')} selected={isTag}> + + ) } diff --git a/src/views/Follow/FollowFeed/TagsFeed/index.tsx b/src/views/Follow/FollowFeed/TagsFeed/index.tsx new file mode 100644 index 0000000000..11962e7261 --- /dev/null +++ b/src/views/Follow/FollowFeed/TagsFeed/index.tsx @@ -0,0 +1,194 @@ +import { useQuery } from '@apollo/react-hooks' +import gql from 'graphql-tag' +import _find from 'lodash/find' +import _intersection from 'lodash/intersection' +import _reverse from 'lodash/reverse' +import _sortBy from 'lodash/sortBy' + +import { + ArticleDigestFeed, + EmptyArticle, + InfiniteScroll, + List, + Spinner, + Tag, +} from '~/components' +import { QueryError } from '~/components/GQL' + +import { analytics, mergeConnections } from '~/common/utils' + +import styles from './styles.css' + +import { + FollowTagsArticlesFeed, + FollowTagsArticlesFeed_viewer_recommendation_followTagsArticles_edges_node as FollowTagsArticlesFeedNode, +} from './__generated__/FollowTagsArticlesFeed' +import { FollowTagsFeed } from './__generated__/FollowTagsFeed' + +const FOLLOW_TAGS = gql` + query FollowTagsFeed { + viewer { + id + recommendation { + followTags(input: { first: null }) { + edges { + node { + ... on Tag { + id + } + } + } + } + } + } + } +` + +const FOLLOW_TAGS_ARTICLES = gql` + query FollowTagsArticlesFeed($after: String) { + viewer { + id + recommendation { + followTagsArticles(input: { first: 10, after: $after }) { + pageInfo { + startCursor + endCursor + hasNextPage + } + edges { + cursor + node { + __typename + ... on Article { + tags { + ...DigestTag + createdAt + } + ...ArticleDigestFeedArticle + } + } + } + } + } + } + } + ${ArticleDigestFeed.fragments.article} + ${Tag.fragments.tag} +` + +const TagsArticles = ({ tagIds }: { tagIds: string[] }) => { + const { data, loading, error, fetchMore, refetch } = useQuery< + FollowTagsArticlesFeed + >(FOLLOW_TAGS_ARTICLES) + + if (loading) { + return + } + + if (error) { + return + } + + const connectionPath = 'viewer.recommendation.followTagsArticles' + const { edges, pageInfo } = + data?.viewer?.recommendation.followTagsArticles || {} + + if (!edges || edges.length <= 0 || !pageInfo) { + return + } + + const loadMore = () => { + analytics.trackEvent('load_more', { + type: 'follow', + location: edges.length, + }) + return fetchMore({ + variables: { + after: pageInfo.endCursor, + }, + updateQuery: (previousResult, { fetchMoreResult }) => + mergeConnections({ + oldData: previousResult, + newData: fetchMoreResult, + path: connectionPath, + }), + }) + } + + const TagComponent = ({ node }: { node: FollowTagsArticlesFeedNode }) => { + if (!node || !node.tags || node.tags.length <= 0) { + return null + } + + const tags = _sortBy(node?.tags || [], ['createdAt']) + const matches = _intersection( + tags.map(({ id }) => id), + tagIds + ) + if (!matches || matches.length <= 0) { + return null + } + const tag = _find(tags, { id: matches[0] }) + if (!tag) { + return null + } + return ( +
+ + +
+ ) + } + + return ( + + + {edges.map(({ node, cursor }, i) => ( + + + + analytics.trackEvent('click_feed', { + type: 'follow-article', + contentType: 'article', + styleType: 'no_cover', + location: i, + }) + } + inFollowFeed + /> + + ))} + + + ) +} + +const TagsFeed = () => { + const { data, loading, error } = useQuery(FOLLOW_TAGS) + + if (loading) { + return + } + + if (error) { + return + } + + const { edges } = data?.viewer?.recommendation.followTags || {} + + if (!edges || edges.length <= 0) { + return + } + + const tagIds = edges.map(({ node }) => node.id) + + return +} + +export default TagsFeed diff --git a/src/views/Follow/FollowFeed/TagsFeed/styles.css b/src/views/Follow/FollowFeed/TagsFeed/styles.css new file mode 100644 index 0000000000..4768230f38 --- /dev/null +++ b/src/views/Follow/FollowFeed/TagsFeed/styles.css @@ -0,0 +1,4 @@ +.tag { + width: 100%; + margin: var(--spacing-base) 0 var(--spacing-xx-tight) var(--spacing-base); +} diff --git a/src/views/Follow/FollowFeed/index.tsx b/src/views/Follow/FollowFeed/index.tsx index 7de40bc854..3b4ddd5242 100644 --- a/src/views/Follow/FollowFeed/index.tsx +++ b/src/views/Follow/FollowFeed/index.tsx @@ -1,213 +1,14 @@ import { useQuery } from '@apollo/react-hooks' -import gql from 'graphql-tag' -import { - ArticleDigestFeed, - EmptyArticle, - Head, - InfiniteScroll, - List, - Spinner, -} from '~/components' -import { QueryError } from '~/components/GQL' +import { Head } from '~/components' import CLIENT_PREFERENCE from '~/components/GQL/queries/clientPreference' -import { analytics, mergeConnections } from '~/common/utils' - +import ArticlesFeed from './ArticlesFeed' +import CommentsFeed from './CommentsFeed' import FeedType, { FollowFeedType } from './FeedType' -import FollowComment from './FollowComment' +import TagsFeed from './TagsFeed' import { ClientPreference } from '~/components/GQL/queries/__generated__/ClientPreference' -import { FollowArticleFeed } from './__generated__/FollowArticleFeed' -import { FollowCommentFeed } from './__generated__/FollowCommentFeed' - -const queries = { - article: gql` - query FollowArticleFeed($after: String) { - viewer { - id - recommendation { - followeeArticles(input: { first: 10, after: $after }) { - pageInfo { - startCursor - endCursor - hasNextPage - } - edges { - cursor - node { - __typename - ... on Article { - ...ArticleDigestFeedArticle - } - } - } - } - } - } - } - ${ArticleDigestFeed.fragments.article} - `, - comment: gql` - query FollowCommentFeed($after: String) { - viewer { - id - recommendation { - followeeComments(input: { first: 10, after: $after }) { - pageInfo { - startCursor - endCursor - hasNextPage - } - edges { - cursor - node { - __typename - ... on Comment { - ...FollowComment - } - } - } - } - } - } - } - ${FollowComment.fragments.comment} - `, -} - -const ArticlesFeed = () => { - const { data, loading, error, fetchMore, refetch } = useQuery< - FollowArticleFeed - >(queries.article) - - if (loading) { - return - } - - if (error) { - return - } - - const connectionPath = 'viewer.recommendation.followeeArticles' - const { edges, pageInfo } = - data?.viewer?.recommendation.followeeArticles || {} - - if (!edges || edges.length <= 0 || !pageInfo) { - return - } - - const loadMore = () => { - analytics.trackEvent('load_more', { - type: 'follow', - location: edges.length, - }) - return fetchMore({ - variables: { - after: pageInfo.endCursor, - }, - updateQuery: (previousResult, { fetchMoreResult }) => - mergeConnections({ - oldData: previousResult, - newData: fetchMoreResult, - path: connectionPath, - }), - }) - } - - return ( - - - {edges.map(({ node, cursor }, i) => ( - - - analytics.trackEvent('click_feed', { - type: 'follow-article', - contentType: 'article', - styleType: 'no_cover', - location: i, - }) - } - inFollowFeed - /> - - ))} - - - ) -} - -const CommentsFeed = () => { - const { data, loading, error, fetchMore, refetch } = useQuery< - FollowCommentFeed - >(queries.comment) - - if (loading) { - return - } - - if (error) { - return - } - - const connectionPath = 'viewer.recommendation.followeeComments' - const { edges, pageInfo } = - data?.viewer?.recommendation.followeeComments || {} - - if (!edges || edges.length <= 0 || !pageInfo) { - return - } - - const loadMore = () => { - analytics.trackEvent('load_more', { - type: 'follow', - location: edges.length, - }) - return fetchMore({ - variables: { - after: pageInfo.endCursor, - }, - updateQuery: (previousResult, { fetchMoreResult }) => - mergeConnections({ - oldData: previousResult, - newData: fetchMoreResult, - path: connectionPath, - }), - }) - } - - return ( - - - {edges.map(({ node, cursor }, i) => ( - - - analytics.trackEvent('click_feed', { - type: 'follow-comment', - contentType: 'comment', - styleType: 'card', - location: i, - }) - } - /> - - ))} - - - ) -} const FollowFeed = () => { const { data, client } = useQuery(CLIENT_PREFERENCE, { @@ -236,6 +37,7 @@ const FollowFeed = () => {
{followFeedType === 'article' && } {followFeedType === 'comment' && } + {followFeedType === 'tag' && } ) } diff --git a/src/views/TagDetail/Buttons/AddButton/TagArticleDialog/Content.tsx b/src/views/TagDetail/Buttons/AddButton/TagArticleDialog/Content.tsx index dd74abfba3..20e9081725 100644 --- a/src/views/TagDetail/Buttons/AddButton/TagArticleDialog/Content.tsx +++ b/src/views/TagDetail/Buttons/AddButton/TagArticleDialog/Content.tsx @@ -15,17 +15,21 @@ import { } from '~/components' import { useMutation } from '~/components/GQL' import SEARCH_ARTICLES from '~/components/GQL/queries/searchArticles' +import updateTagArticlesCount from '~/components/GQL/updates/tagArticlesCount' import { ADD_TOAST, REFETCH_TAG_DETAIL_ARTICLES, TEXT } from '~/common/enums' import { parseFormSubmitErrors, translate } from '~/common/utils' import styles from './styles.css' -import { PutArticlesTags } from './__generated__/PutArticlesTags' +import { TagDetail_node_Tag } from '../../../__generated__/TagDetail' +import { AddArticlesTags } from './__generated__/AddArticlesTags' -const PUT_ARTICLES_TAGS = gql` - mutation PutArticlesTags($id: ID!, $articles: [ID!], $selected: Boolean!) { - putArticlesTags(input: { id: $id, articles: $articles }) { +const ADD_ARTICLES_TAGS = gql` + mutation AddArticlesTags($id: ID!, $articles: [ID!], $selected: Boolean) { + addArticlesTags( + input: { id: $id, articles: $articles, selected: $selected } + ) { id articles(input: { first: 0, selected: $selected }) { totalCount @@ -58,9 +62,9 @@ const DropdownContent = ({ } interface TagArticleDialogContentProps { - id?: string closeDialog: () => void forSelected?: boolean + tag: TagDetail_node_Tag } interface FormValues { @@ -70,12 +74,12 @@ interface FormValues { const TagArticleDialogContent: React.FC = ({ closeDialog, - id, forSelected = false, + tag, }) => { const isSmallUp = useResponsive('sm-up') const [selectedArticles, setSelectedArticles] = useState([]) - const [update] = useMutation(PUT_ARTICLES_TAGS) + const [add] = useMutation(ADD_ARTICLES_TAGS) const { lang } = useContext(LanguageContext) const formId = 'put-article-tag-form' @@ -108,11 +112,25 @@ const TagArticleDialogContent: React.FC = ({ }, onSubmit: async ({ name, articles }, { setFieldError, setSubmitting }) => { try { - if (!id) { + if (!tag.id) { return } - await update({ variables: { id, articles, selected: forSelected } }) + await add({ + variables: { id: tag.id, articles, selected: forSelected }, + update: (cache, { data }) => { + if (forSelected) { + const newCount = data?.addArticlesTags?.articles?.totalCount || 0 + const oldCount = tag.articles.totalCount || 0 + updateTagArticlesCount({ + cache, + id: tag.id, + count: newCount - oldCount, + type: 'increment', + }) + } + }, + }) setSubmitting(false) diff --git a/src/views/TagDetail/Buttons/AddButton/TagArticleDialog/index.tsx b/src/views/TagDetail/Buttons/AddButton/TagArticleDialog/index.tsx index 4f21e37640..362f08a5f0 100644 --- a/src/views/TagDetail/Buttons/AddButton/TagArticleDialog/index.tsx +++ b/src/views/TagDetail/Buttons/AddButton/TagArticleDialog/index.tsx @@ -4,16 +4,18 @@ import { Dialog } from '~/components' import Content from './Content' +import { TagDetail_node_Tag } from '../../../__generated__/TagDetail' + interface TagArticleDialogProps { - id?: string children: ({ open }: { open: () => void }) => React.ReactNode forSelected?: boolean + tag: TagDetail_node_Tag } const TagArticleDialog = ({ - id, children, forSelected, + tag, }: TagArticleDialogProps) => { const [showDialog, setShowDialog] = useState(true) const open = () => setShowDialog(true) @@ -24,7 +26,7 @@ const TagArticleDialog = ({ {children({ open })} - + ) diff --git a/src/views/TagDetail/Buttons/AddButton/index.tsx b/src/views/TagDetail/Buttons/AddButton/index.tsx index 2ed4da53e8..469218f2de 100644 --- a/src/views/TagDetail/Buttons/AddButton/index.tsx +++ b/src/views/TagDetail/Buttons/AddButton/index.tsx @@ -1,18 +1,39 @@ +import { useContext } from 'react' + import { Button, DropdownDialog, IconAddMedium, IconHashTag, IconPen, + IconSpinner, + LanguageContext, + LikeCoinDialog, Menu, TextIcon, Translate, + ViewerContext, } from '~/components' +import { useMutation } from '~/components/GQL' +import CREATE_DRAFT from '~/components/GQL/mutations/createDraft' + +import { ADD_TOAST } from '~/common/enums' +import { + analytics, + parseFormSubmitErrors, + routerPush, + toPath, + translate, +} from '~/common/utils' import TagArticleDialog from './TagArticleDialog' +import { CreateDraft } from '~/components/GQL/mutations/__generated__/CreateDraft' +import { TagDetail_node_Tag } from '../../__generated__/TagDetail' + interface DropdownActionsProps { - id: string + isMaintainer: boolean + tag: TagDetail_node_Tag } interface DialogProps { @@ -22,24 +43,104 @@ interface DialogProps { type BaseDropdownActionsProps = DropdownActionsProps & DialogProps +const BaseCreateDraftMenuItem = ({ onClick }: { onClick: () => any }) => ( + + } size="md" spacing="base"> + + + +) + +const CreateDraftMenuItem = ({ + putDraft, +}: { + putDraft: () => Promise +}) => { + const { lang } = useContext(LanguageContext) + const viewer = useContext(ViewerContext) + + if (viewer.shouldSetupLikerID) { + return ( + + {({ open }) => } + + ) + } + + return ( + { + try { + if (viewer.isInactive) { + window.dispatchEvent( + new CustomEvent(ADD_TOAST, { + detail: { + color: 'red', + content: , + }, + }) + ) + return + } + + analytics.trackEvent('click_button', { + type: 'write', + }) + const result = await putDraft() + const { slug, id } = result?.data?.putDraft || {} + + if (slug && id) { + const path = toPath({ page: 'draftDetail', slug, id }) + routerPush(path.href, path.as) + } + } catch (error) { + const [messages, codes] = parseFormSubmitErrors(error, lang) + + if (!messages[codes[0]]) { + return null + } + + window.dispatchEvent( + new CustomEvent(ADD_TOAST, { + detail: { + color: 'red', + content: messages[codes[0]], + }, + }) + ) + } + }} + /> + ) +} + const BaseDropdownActions = ({ - id, + isMaintainer, + tag, openTagSelectedArticleDialog, openTagArticleDialog, }: BaseDropdownActionsProps) => { + const { lang } = useContext(LanguageContext) + const [putDraft, { loading }] = useMutation(CREATE_DRAFT, { + variables: { + title: translate({ id: 'untitle', lang }), + tags: [tag.content], + }, + }) + const Content = ({ isInDropdown }: { isInDropdown?: boolean }) => ( - - } size="md" spacing="base"> - - - - - - } size="md" spacing="base"> - - - + {isMaintainer && ( + <> + + } size="md" spacing="base"> + + + + + + )} + } size="md" spacing="base"> @@ -70,7 +171,11 @@ const BaseDropdownActions = ({ aria-haspopup="true" ref={ref} > - } weight="md" size="md-s"> + : } + weight="md" + size="md-s" + > @@ -81,9 +186,9 @@ const BaseDropdownActions = ({ const DropdownActions = (props: DropdownActionsProps) => { return ( - + {({ open: openTagSelectedArticleDialog }) => ( - + {({ open: openTagArticleDialog }) => ( { @@ -42,7 +42,7 @@ const BaseDropdownActions = ({ - {canEdit && ( + {isMaintainer && ( } size="md" spacing="base"> diff --git a/src/views/TagDetail/index.tsx b/src/views/TagDetail/index.tsx index e86933c1ec..3cbdd8e298 100644 --- a/src/views/TagDetail/index.tsx +++ b/src/views/TagDetail/index.tsx @@ -83,15 +83,16 @@ const TagDetail = ({ data }: { data: TagDetailType }) => { setFeed('latest') } + const filter = ({ displayName }: any) => (displayName || '').toLowerCase() !== 'matty' const editors = data.node.editors || [] - const maintainer = _find( - editors, - (editor) => (editor.displayName || '').toLowerCase() !== 'matty' - ) + const owner = _find(editors, filter) + + // define permission + const normalEditors = editors.filter(filter) const isEditor = _some(editors, (editor) => editor.id === viewer.id) const isCreator = data.node.creator?.id === viewer.id - const canEdit = - isEditor || isCreator || viewer.info.email === 'hi@matters.news' + const isMaintainer = + isEditor || (normalEditors.length === 0 && isCreator) || viewer.info.email === 'hi@matters.news' return ( @@ -105,7 +106,7 @@ const TagDetail = ({ data }: { data: TagDetailType }) => { id={data.node.id} content={data.node.content} description={data.node.description || undefined} - canEdit={canEdit} + isMaintainer={isMaintainer} /> } @@ -117,10 +118,10 @@ const TagDetail = ({ data }: { data: TagDetailType }) => {
- {maintainer && ( -
+ {owner && ( +
{
- {canEdit && } +
diff --git a/src/views/TagDetail/styles.css b/src/views/TagDetail/styles.css index 21db4bdc9c..27889f2a42 100644 --- a/src/views/TagDetail/styles.css +++ b/src/views/TagDetail/styles.css @@ -5,7 +5,7 @@ margin-bottom: var(--spacing-x-tight); } -.maintainer { +.owner { @mixin flex-center-start; padding: 0 0 var(--spacing-base); From 23ff1d5eb1b10a51ab27862ce626d0db6c0e27da Mon Sep 17 00:00:00 2001 From: Zeck Li Date: Thu, 9 Jul 2020 17:42:29 +0800 Subject: [PATCH 16/86] style(component): formatting --- src/views/TagDetail/Buttons/AddButton/index.tsx | 6 +++++- src/views/TagDetail/index.tsx | 12 +++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/views/TagDetail/Buttons/AddButton/index.tsx b/src/views/TagDetail/Buttons/AddButton/index.tsx index 469218f2de..86b3978ed3 100644 --- a/src/views/TagDetail/Buttons/AddButton/index.tsx +++ b/src/views/TagDetail/Buttons/AddButton/index.tsx @@ -133,7 +133,11 @@ const BaseDropdownActions = ({ {isMaintainer && ( <> - } size="md" spacing="base"> + } + size="md" + spacing="base" + > diff --git a/src/views/TagDetail/index.tsx b/src/views/TagDetail/index.tsx index 3cbdd8e298..965e0e3a28 100644 --- a/src/views/TagDetail/index.tsx +++ b/src/views/TagDetail/index.tsx @@ -83,7 +83,8 @@ const TagDetail = ({ data }: { data: TagDetailType }) => { setFeed('latest') } - const filter = ({ displayName }: any) => (displayName || '').toLowerCase() !== 'matty' + const filter = ({ displayName }: any) => + (displayName || '').toLowerCase() !== 'matty' const editors = data.node.editors || [] const owner = _find(editors, filter) @@ -92,7 +93,9 @@ const TagDetail = ({ data }: { data: TagDetailType }) => { const isEditor = _some(editors, (editor) => editor.id === viewer.id) const isCreator = data.node.creator?.id === viewer.id const isMaintainer = - isEditor || (normalEditors.length === 0 && isCreator) || viewer.info.email === 'hi@matters.news' + isEditor || + (normalEditors.length === 0 && isCreator) || + viewer.info.email === 'hi@matters.news' return ( @@ -147,7 +150,10 @@ const TagDetail = ({ data }: { data: TagDetailType }) => {
- +
From 937c312a81002486c8b9d274046929223886e304 Mon Sep 17 00:00:00 2001 From: Zeck Li Date: Thu, 9 Jul 2020 22:29:13 +0800 Subject: [PATCH 17/86] refactor(schema): update schema for Tag APIs --- .../Follow/FollowFeed/TagsFeed/index.tsx | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/views/Follow/FollowFeed/TagsFeed/index.tsx b/src/views/Follow/FollowFeed/TagsFeed/index.tsx index 11962e7261..8eff359524 100644 --- a/src/views/Follow/FollowFeed/TagsFeed/index.tsx +++ b/src/views/Follow/FollowFeed/TagsFeed/index.tsx @@ -20,17 +20,17 @@ import { analytics, mergeConnections } from '~/common/utils' import styles from './styles.css' import { - FollowTagsArticlesFeed, - FollowTagsArticlesFeed_viewer_recommendation_followTagsArticles_edges_node as FollowTagsArticlesFeedNode, -} from './__generated__/FollowTagsArticlesFeed' -import { FollowTagsFeed } from './__generated__/FollowTagsFeed' + FollowingTagsArticlesFeed, + FollowingTagsArticlesFeed_viewer_recommendation_followingTagsArticles_edges_node as FollowingTagsArticlesFeedNode, +} from './__generated__/FollowingTagsArticlesFeed' +import { FollowingTagsFeed } from './__generated__/FollowingTagsFeed' -const FOLLOW_TAGS = gql` - query FollowTagsFeed { +const FOLLOWING_TAGS = gql` + query FollowingTagsFeed { viewer { id recommendation { - followTags(input: { first: null }) { + followingTags(input: { first: null }) { edges { node { ... on Tag { @@ -44,12 +44,12 @@ const FOLLOW_TAGS = gql` } ` -const FOLLOW_TAGS_ARTICLES = gql` - query FollowTagsArticlesFeed($after: String) { +const FOLLOWING_TAGS_ARTICLES = gql` + query FollowingTagsArticlesFeed($after: String) { viewer { id recommendation { - followTagsArticles(input: { first: 10, after: $after }) { + followingTagsArticles(input: { first: 10, after: $after }) { pageInfo { startCursor endCursor @@ -78,8 +78,8 @@ const FOLLOW_TAGS_ARTICLES = gql` const TagsArticles = ({ tagIds }: { tagIds: string[] }) => { const { data, loading, error, fetchMore, refetch } = useQuery< - FollowTagsArticlesFeed - >(FOLLOW_TAGS_ARTICLES) + FollowingTagsArticlesFeed + >(FOLLOWING_TAGS_ARTICLES) if (loading) { return @@ -89,9 +89,9 @@ const TagsArticles = ({ tagIds }: { tagIds: string[] }) => { return } - const connectionPath = 'viewer.recommendation.followTagsArticles' + const connectionPath = 'viewer.recommendation.followingTagsArticles' const { edges, pageInfo } = - data?.viewer?.recommendation.followTagsArticles || {} + data?.viewer?.recommendation.followingTagsArticles || {} if (!edges || edges.length <= 0 || !pageInfo) { return @@ -115,7 +115,7 @@ const TagsArticles = ({ tagIds }: { tagIds: string[] }) => { }) } - const TagComponent = ({ node }: { node: FollowTagsArticlesFeedNode }) => { + const TagComponent = ({ node }: { node: FollowingTagsArticlesFeedNode }) => { if (!node || !node.tags || node.tags.length <= 0) { return null } @@ -170,7 +170,7 @@ const TagsArticles = ({ tagIds }: { tagIds: string[] }) => { } const TagsFeed = () => { - const { data, loading, error } = useQuery(FOLLOW_TAGS) + const { data, loading, error } = useQuery(FOLLOWING_TAGS) if (loading) { return @@ -180,7 +180,7 @@ const TagsFeed = () => { return } - const { edges } = data?.viewer?.recommendation.followTags || {} + const { edges } = data?.viewer?.recommendation.followingTags || {} if (!edges || edges.length <= 0) { return From 5b12b68781ea7b844b8333c5f350ef3998f3ed16 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 9 Jul 2020 22:15:43 +0000 Subject: [PATCH 18/86] build(deps): bump graphql-tag from 2.10.3 to 2.10.4 Bumps [graphql-tag](https://github.com/apollographql/graphql-tag) from 2.10.3 to 2.10.4. - [Release notes](https://github.com/apollographql/graphql-tag/releases) - [Changelog](https://github.com/apollographql/graphql-tag/blob/master/CHANGELOG.md) - [Commits](https://github.com/apollographql/graphql-tag/commits) Signed-off-by: dependabot-preview[bot] --- package-lock.json | 12 +++++++++--- package.json | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index d135b7b3be..5b8932b979 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6630,6 +6630,12 @@ "path-is-absolute": "^1.0.0" } }, + "graphql-tag": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.10.3.tgz", + "integrity": "sha512-4FOv3ZKfA4WdOKJeHdz6B3F/vxBLSgmBcGeAFPf4n1F64ltJUvOOerNj0rsJxONQGdhUMynQIvd6LzB+1J5oKA==", + "dev": true + }, "mkdirp": { "version": "0.5.5", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", @@ -12927,9 +12933,9 @@ } }, "graphql-tag": { - "version": "2.10.3", - "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.10.3.tgz", - "integrity": "sha512-4FOv3ZKfA4WdOKJeHdz6B3F/vxBLSgmBcGeAFPf4n1F64ltJUvOOerNj0rsJxONQGdhUMynQIvd6LzB+1J5oKA==" + "version": "2.10.4", + "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.10.4.tgz", + "integrity": "sha512-O7vG5BT3w6Sotc26ybcvLKNTdfr4GfsIVMD+LdYqXCeJIYPRyp8BIsDOUtxw7S1PYvRw5vH3278J2EDezR6mfA==" }, "growl": { "version": "1.10.5", diff --git a/package.json b/package.json index 67b0931948..44af5eef76 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "firebase": "^7.15.5", "formik": "^2.1.4", "graphql": "^14.7.0", - "graphql-tag": "^2.10.3", + "graphql-tag": "^2.10.4", "helmet": "^3.23.3", "isomorphic-unfetch": "^3.0.0", "jump.js": "^1.0.2", From ae93d84f3a26447eecaeeca2aea5c996e91dc9a5 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 10 Jul 2020 01:39:18 +0000 Subject: [PATCH 19/86] build(deps): bump firebase from 7.15.5 to 7.16.0 Bumps [firebase](https://github.com/firebase/firebase-js-sdk) from 7.15.5 to 7.16.0. - [Release notes](https://github.com/firebase/firebase-js-sdk/releases) - [Commits](https://github.com/firebase/firebase-js-sdk/compare/firebase@7.15.5...firebase@7.16.0) Signed-off-by: dependabot-preview[bot] --- package-lock.json | 166 +++++++++++++++++++++++----------------------- package.json | 2 +- 2 files changed, 84 insertions(+), 84 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5b8932b979..13fb948c37 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1739,14 +1739,14 @@ } }, "@firebase/analytics": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.3.8.tgz", - "integrity": "sha512-HpNRBJHnrGq5jtVTNRgA8Ozng2ilt0pkej8D5EvXoaylu80U+ICKLBlIT8TdUSEfkXC/RPjvLXg6vn/sq/CyqA==", + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.3.9.tgz", + "integrity": "sha512-l4dNskm8uQ+UqO6Lw+fuyO1enZBXUV6xNMxeVABEnVrp3wOP90KKb/ZwYgleAxF1It52lorcTtkA1YFpv3iEIQ==", "requires": { "@firebase/analytics-types": "0.3.1", - "@firebase/component": "0.1.15", - "@firebase/installations": "0.4.13", - "@firebase/logger": "0.2.5", + "@firebase/component": "0.1.16", + "@firebase/installations": "0.4.14", + "@firebase/logger": "0.2.6", "@firebase/util": "0.2.50", "tslib": "^1.11.1" } @@ -1757,13 +1757,13 @@ "integrity": "sha512-63vVJ5NIBh/JF8l9LuPrQYSzFimk7zYHySQB4Dk9rVdJ8kV/vGQoVTvRu1UW05sEc2Ug5PqtEChtTHU+9hvPcA==" }, "@firebase/app": { - "version": "0.6.7", - "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.6.7.tgz", - "integrity": "sha512-6NpIZ3iMrCR2XOShK5oi3YYB0GXX5yxVD8p3+2N+X4CF5cERyIrDRf8+YXOFgr+bDHSbVcIyzpWv6ijhg4MJlw==", + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.6.8.tgz", + "integrity": "sha512-Tm7Pi6Dtpx4FFKcpm0jcrZ/qI9oREBxmP3pWlw1jgDW4syRJHmN9/5DYvfFk6FAhj3FrY8E/6F+ngWJfqONotQ==", "requires": { "@firebase/app-types": "0.6.1", - "@firebase/component": "0.1.15", - "@firebase/logger": "0.2.5", + "@firebase/component": "0.1.16", + "@firebase/logger": "0.2.6", "@firebase/util": "0.2.50", "dom-storage": "2.1.0", "tslib": "^1.11.1", @@ -1776,9 +1776,9 @@ "integrity": "sha512-L/ZnJRAq7F++utfuoTKX4CLBG5YR7tFO3PLzG1/oXXKEezJ0kRL3CMRoueBEmTCzVb/6SIs2Qlaw++uDgi5Xyg==" }, "@firebase/auth": { - "version": "0.14.7", - "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-0.14.7.tgz", - "integrity": "sha512-NTQY9luV70XUA6zGYOWloDSaOT+l0/R4u3W7ptqVCfZNc4DAt7euUkTbj7SDD14902sHF54j+tk5kmpEmMd0jA==", + "version": "0.14.8", + "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-0.14.8.tgz", + "integrity": "sha512-LufoWcFpYAoCUkFDKSELH69xI8NdOjNTUFKvWfADZN7ysr4dpPdDs2ZYnH67FqcMb0tX+Jdx6vWrF6VZ37AAJQ==", "requires": { "@firebase/auth-types": "0.10.1" } @@ -1794,23 +1794,23 @@ "integrity": "sha512-/+gBHb1O9x/YlG7inXfxff/6X3BPZt4zgBv4kql6HEmdzNQCodIRlEYnI+/da+lN+dha7PjaFH7C7ewMmfV7rw==" }, "@firebase/component": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.1.15.tgz", - "integrity": "sha512-HqFb1qQl1vtlUMIzPM15plNz27jqM8DWjuQQuGeDfG+4iRRflwKfgNw1BOyoP4kQ8vOBCL7t/71yPXSomNdJdQ==", + "version": "0.1.16", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.1.16.tgz", + "integrity": "sha512-FvffvFN0LWgv1H/FIyruTECOL69Dhy+JfwoTq+mV39V8Mz9lNpo41etonL5AOr7KmXxYJVbNwkx0L9Ei88i7JA==", "requires": { "@firebase/util": "0.2.50", "tslib": "^1.11.1" } }, "@firebase/database": { - "version": "0.6.6", - "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.6.6.tgz", - "integrity": "sha512-TqUJOaCATF/h3wpqhPT9Fz1nZI6gBv/M2pHZztUjX4A9o9Bq93NyqUurYiZnGB7zpSkEADFCVT4f0VBrWdHlNw==", + "version": "0.6.7", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.6.7.tgz", + "integrity": "sha512-vm0ch2zNSoHfXWnDG6WVjf0p/BdXOMBL1lAfkGu3DYH/Rkl4p97x57w0WNOURNfL4GY2LIqScSYKCidV7jqTog==", "requires": { "@firebase/auth-interop-types": "0.1.5", - "@firebase/component": "0.1.15", + "@firebase/component": "0.1.16", "@firebase/database-types": "0.5.1", - "@firebase/logger": "0.2.5", + "@firebase/logger": "0.2.6", "@firebase/util": "0.2.50", "faye-websocket": "0.11.3", "tslib": "^1.11.1" @@ -1825,13 +1825,13 @@ } }, "@firebase/firestore": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-1.15.5.tgz", - "integrity": "sha512-unkRIC2hL2Ge5er/Hj43aUYiEKlW5bpju8TnIaF33avg/wZpSsmtVrMlAQVkBWFhvWeYpJSr2QOzNLa1bQvuCA==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-1.16.0.tgz", + "integrity": "sha512-RsgGIP9e6HW5soEHIuo0CGVFpeTKe0hqjrgOLk92W0mjL6irzBlqmd5HcGMY4F5QiZryc2vMT1/3LvRhkUyf8g==", "requires": { - "@firebase/component": "0.1.15", - "@firebase/firestore-types": "1.11.0", - "@firebase/logger": "0.2.5", + "@firebase/component": "0.1.16", + "@firebase/firestore-types": "1.12.0", + "@firebase/logger": "0.2.6", "@firebase/util": "0.2.50", "@firebase/webchannel-wrapper": "0.2.41", "@grpc/grpc-js": "^1.0.0", @@ -1840,16 +1840,16 @@ } }, "@firebase/firestore-types": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-1.11.0.tgz", - "integrity": "sha512-hD7+cmMUvT5OJeWVrcRkE87PPuj/0/Wic6bntCopJE1WIX/Dm117AUkHgKd3S7Ici6DLp4bdlx1MjjwWL5942w==" + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-1.12.0.tgz", + "integrity": "sha512-OqNxVb63wPZdUc7YnpacAW1WNIMSKERSewCRi+unCQ0YI0KNfrDSypyGCyel+S3GdOtKMk9KnvDknaGbnaFX4g==" }, "@firebase/functions": { - "version": "0.4.47", - "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.4.47.tgz", - "integrity": "sha512-wiyMezW1EYq80Uk15M4poapCG10PjN5UJEY0jJr7DhCnDAoADMGlsIYFYio60+biGreij5/hpOybw5mU9WpXUw==", + "version": "0.4.48", + "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.4.48.tgz", + "integrity": "sha512-BwI/JzO/f/nquKG1IS3VqmwMaKEhvM58/08vTnp46krHBsOYqsdD9T2amz+HXGT9fe2HhDsUhgFE8D00S0vqbg==", "requires": { - "@firebase/component": "0.1.15", + "@firebase/component": "0.1.16", "@firebase/functions-types": "0.3.17", "@firebase/messaging-types": "0.4.5", "isomorphic-fetch": "2.2.1", @@ -1862,11 +1862,11 @@ "integrity": "sha512-DGR4i3VI55KnYk4IxrIw7+VG7Q3gA65azHnZxo98Il8IvYLr2UTBlSh72dTLlDf25NW51HqvJgYJDKvSaAeyHQ==" }, "@firebase/installations": { - "version": "0.4.13", - "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.4.13.tgz", - "integrity": "sha512-Sic7BtWgdUwk+Z1C4L49Edkhzaol/ijEIdv0pkHfjedIPirIU2V8CJ5qykx2y4aTiyVbdFqfjIpp1c6A6W3GBA==", + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.4.14.tgz", + "integrity": "sha512-hQPsaU7wdTq3CFMtFQwZy6LgdXZAkXoUToV4O+ekPbjM65QzaGVogJVU8O2H6ADXoq37SarcUXKe86pcUWdFLA==", "requires": { - "@firebase/component": "0.1.15", + "@firebase/component": "0.1.16", "@firebase/installations-types": "0.3.4", "@firebase/util": "0.2.50", "idb": "3.0.2", @@ -1879,17 +1879,17 @@ "integrity": "sha512-RfePJFovmdIXb6rYwtngyxuEcWnOrzdZd9m7xAW0gRxDIjBT20n3BOhjpmgRWXo/DAxRmS7bRjWAyTHY9cqN7Q==" }, "@firebase/logger": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.2.5.tgz", - "integrity": "sha512-qqw3m0tWs/qrg7axTZG/QZq24DIMdSY6dGoWuBn08ddq7+GLF5HiqkRj71XznYeUUbfRq5W9C/PSFnN4JxX+WA==" + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.2.6.tgz", + "integrity": "sha512-KIxcUvW/cRGWlzK9Vd2KB864HlUnCfdTH0taHE0sXW5Xl7+W68suaeau1oKNEqmc3l45azkd4NzXTCWZRZdXrw==" }, "@firebase/messaging": { - "version": "0.6.19", - "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.6.19.tgz", - "integrity": "sha512-PhqK69m70G+GGgvbdnGz2+PyoqfmR5b+nouj1JV+HgyBCjMAhF8rDYQzCWWgy4HaWbLoS/xW6AZUKG20Kv2H1A==", + "version": "0.6.20", + "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.6.20.tgz", + "integrity": "sha512-1MqyljXnbFBeHYhL6QInVM9aO5MW820yhNmOIVxk58wNXq4tOQLzqnKuvlgZ+ttgqlDzrIYiVf3EOHh5DptttQ==", "requires": { - "@firebase/component": "0.1.15", - "@firebase/installations": "0.4.13", + "@firebase/component": "0.1.16", + "@firebase/installations": "0.4.14", "@firebase/messaging-types": "0.4.5", "@firebase/util": "0.2.50", "idb": "3.0.2", @@ -1902,13 +1902,13 @@ "integrity": "sha512-sux4fgqr/0KyIxqzHlatI04Ajs5rc3WM+WmtCpxrKP1E5Bke8xu/0M+2oy4lK/sQ7nov9z15n3iltAHCgTRU3Q==" }, "@firebase/performance": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.3.8.tgz", - "integrity": "sha512-jODXrtFLyfnRiBehHuMBmsBtMv38U9sTictRxJSz+9JahvWYm1AF0YDzPlfeyYj+kxM6+S5wdQxUaPVdcWAvWg==", + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.3.9.tgz", + "integrity": "sha512-Fj22DZXRhhKv1OSUzDxX7AqpJUcDld6tzXK1yxOC8e3v1DFPQMQdM9FoG1m1b/Vrqa6pCCqnqG6gh6VPnEcAzQ==", "requires": { - "@firebase/component": "0.1.15", - "@firebase/installations": "0.4.13", - "@firebase/logger": "0.2.5", + "@firebase/component": "0.1.16", + "@firebase/installations": "0.4.14", + "@firebase/logger": "0.2.6", "@firebase/performance-types": "0.0.13", "@firebase/util": "0.2.50", "tslib": "^1.11.1" @@ -1942,13 +1942,13 @@ } }, "@firebase/remote-config": { - "version": "0.1.24", - "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.1.24.tgz", - "integrity": "sha512-/Kd+I5mNPI2wJJFySOC8Mjj4lRnEwZhU0RteuVlzFCDWWEyTE//r+p2TLAufQ9J+Fd3Ru5fVMFLNyU8k71Viiw==", + "version": "0.1.25", + "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.1.25.tgz", + "integrity": "sha512-8YWefBhy77HMbWXWdbenalx+IDY/XkS+iURQ9qRYvSIFYx6RL04DzlakZNOY9CQAcxTA+cTSt4NNlhjopBjf2Q==", "requires": { - "@firebase/component": "0.1.15", - "@firebase/installations": "0.4.13", - "@firebase/logger": "0.2.5", + "@firebase/component": "0.1.16", + "@firebase/installations": "0.4.14", + "@firebase/logger": "0.2.6", "@firebase/remote-config-types": "0.1.9", "@firebase/util": "0.2.50", "tslib": "^1.11.1" @@ -1960,11 +1960,11 @@ "integrity": "sha512-G96qnF3RYGbZsTRut7NBX0sxyczxt1uyCgXQuH/eAfUCngxjEGcZQnBdy6mvSdqdJh5mC31rWPO4v9/s7HwtzA==" }, "@firebase/storage": { - "version": "0.3.37", - "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.3.37.tgz", - "integrity": "sha512-RLbiRQlnvXRP/30OaEiUoRHBxZygqrZyotPPWD2WmD3JMM9qGTVpYNQ092mqL3R8ViyejwlpjlPvrDo7Z9BzgQ==", + "version": "0.3.38", + "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.3.38.tgz", + "integrity": "sha512-gWVQr5xqrU3cfhhwbAE+9iJ0XMvzbxWMvteKurn5cRNaGbmSob/O/ISOAvsQgPnk+K9zPMd2OwyzaTOl9PEMrw==", "requires": { - "@firebase/component": "0.1.15", + "@firebase/component": "0.1.16", "@firebase/storage-types": "0.3.12", "@firebase/util": "0.2.50", "tslib": "^1.11.1" @@ -1989,9 +1989,9 @@ "integrity": "sha512-XcdMT5PSZHiuf7LJIhzKIe+RyYa25S3LHRRvLnZc6iFjwXkrSDJ8J/HWO6VT8d2ZTbawp3VcLEjRF/VN8glCrA==" }, "@grpc/grpc-js": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.1.1.tgz", - "integrity": "sha512-mhZRszS0SKwnWPJaNyrECePZ9U7vaHFGqrzxQbWinWR3WznBIU+nmh2L5J3elF+lp5DEUIzARXkifbs6LQVAHA==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.1.2.tgz", + "integrity": "sha512-k2u86Bkm/3xrjUaSWeIyzXScBt/cC8uE7BznR0cpueQi11R33W6qfJdMrkrsmSHirp5likR55JSXUrcWG6ybHA==", "requires": { "semver": "^6.2.0" } @@ -12194,23 +12194,23 @@ "integrity": "sha512-H1k/ESTD2rJ3liupyqWBPjZC+LKfCGixQzz/NDN4dkgbmG1bVFyMOh7luKSkVDoyfhgvRm62pviNMPI+eJTZcQ==" }, "firebase": { - "version": "7.15.5", - "resolved": "https://registry.npmjs.org/firebase/-/firebase-7.15.5.tgz", - "integrity": "sha512-yeXo3KDp/ZWO0/Uyen99cUvGM76femebmyNOBTHcGSDkBXvIGth6235KhclxLROIKCC5b3YNwmKX11tbaC6RJg==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/firebase/-/firebase-7.16.0.tgz", + "integrity": "sha512-fYimLYkY0SS/jv4+ZnSp5u2+QdtwsTtLwPUfmWiKQnjqas1M5mqhQr3QB7vPQuSANhC3UJZZ5KPxLbFomMJLcA==", "requires": { - "@firebase/analytics": "0.3.8", - "@firebase/app": "0.6.7", + "@firebase/analytics": "0.3.9", + "@firebase/app": "0.6.8", "@firebase/app-types": "0.6.1", - "@firebase/auth": "0.14.7", - "@firebase/database": "0.6.6", - "@firebase/firestore": "1.15.5", - "@firebase/functions": "0.4.47", - "@firebase/installations": "0.4.13", - "@firebase/messaging": "0.6.19", - "@firebase/performance": "0.3.8", + "@firebase/auth": "0.14.8", + "@firebase/database": "0.6.7", + "@firebase/firestore": "1.16.0", + "@firebase/functions": "0.4.48", + "@firebase/installations": "0.4.14", + "@firebase/messaging": "0.6.20", + "@firebase/performance": "0.3.9", "@firebase/polyfill": "0.3.36", - "@firebase/remote-config": "0.1.24", - "@firebase/storage": "0.3.37", + "@firebase/remote-config": "0.1.25", + "@firebase/storage": "0.3.38", "@firebase/util": "0.2.50" } }, @@ -21589,9 +21589,9 @@ }, "dependencies": { "@types/node": { - "version": "13.13.12", - "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.12.tgz", - "integrity": "sha512-zWz/8NEPxoXNT9YyF2osqyA9WjssZukYpgI4UYZpOjcyqwIUqWGkcCionaEb9Ki+FULyPyvNFpg/329Kd2/pbw==" + "version": "13.13.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.13.tgz", + "integrity": "sha512-UfvBE9oRCAJVzfR+3eWm/sdLFe/qroAPEXP3GPJ1SehQiEVgZT6NQZWYbPMiJ3UdcKM06v4j+S1lTcdWCmw+3g==" } } }, diff --git a/package.json b/package.json index 44af5eef76..43e1e673ca 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "date-fns": "^2.14.0", "express": "^4.17.1", "fingerprintjs2": "^2.1.0", - "firebase": "^7.15.5", + "firebase": "^7.16.0", "formik": "^2.1.4", "graphql": "^14.7.0", "graphql-tag": "^2.10.4", From 48971f05a59d374e46a550d28856a6b272a21f24 Mon Sep 17 00:00:00 2001 From: Zeck Li Date: Fri, 10 Jul 2020 18:46:49 +0800 Subject: [PATCH 20/86] fix(component): correct message and description for tag detail page --- src/common/enums/text.ts | 4 ++-- src/components/Empty/EmptyFollowingTag.tsx | 13 +++++++++++++ src/components/Empty/index.tsx | 1 + src/views/Follow/FollowFeed/TagsFeed/index.tsx | 6 +++--- 4 files changed, 19 insertions(+), 5 deletions(-) create mode 100644 src/components/Empty/EmptyFollowingTag.tsx diff --git a/src/common/enums/text.ts b/src/common/enums/text.ts index a79508afc1..4b35ff3732 100644 --- a/src/common/enums/text.ts +++ b/src/common/enums/text.ts @@ -235,7 +235,7 @@ export const TEXT = { tagDescriptionPlaceholder: '輸入一段標籤描述…', tagEdited: '標籤已更新', tagName: '標籤名稱', - tagAddArticle: '添加已有作品', + tagAddArticle: '添加我的作品', tagAddSelectedArticle: '添加精選內容', term: '用戶協議', termAndPrivacy: '用戶協議與隱私政策', @@ -510,7 +510,7 @@ export const TEXT = { tagDescriptionPlaceholder: '输入一段话题描述…', tagEdited: '标签已更新', tagName: '标签名称', - tagAddArticle: '添加已有作品', + tagAddArticle: '添加我的作品', tagAddSelectedArticle: '添加精选內容', term: '用户协议', termAndPrivacy: '用户协议与隐私政策', diff --git a/src/components/Empty/EmptyFollowingTag.tsx b/src/components/Empty/EmptyFollowingTag.tsx new file mode 100644 index 0000000000..196730d874 --- /dev/null +++ b/src/components/Empty/EmptyFollowingTag.tsx @@ -0,0 +1,13 @@ +import { Empty, IconEmptyWarning, Translate } from '~/components' + +export const EmptyFollowingTag = () => ( + } + description={ + + } + /> +) diff --git a/src/components/Empty/index.tsx b/src/components/Empty/index.tsx index 3f4703a071..f644b1fe3f 100644 --- a/src/components/Empty/index.tsx +++ b/src/components/Empty/index.tsx @@ -4,6 +4,7 @@ export * from './EmptyArticle' export * from './EmptyBookmark' export * from './EmptyComment' export * from './EmptyDraft' +export * from './EmptyFollowingTag' export * from './EmptyHistory' export * from './EmptyNotice' export * from './EmptyResponse' diff --git a/src/views/Follow/FollowFeed/TagsFeed/index.tsx b/src/views/Follow/FollowFeed/TagsFeed/index.tsx index 8eff359524..99e169029b 100644 --- a/src/views/Follow/FollowFeed/TagsFeed/index.tsx +++ b/src/views/Follow/FollowFeed/TagsFeed/index.tsx @@ -7,7 +7,7 @@ import _sortBy from 'lodash/sortBy' import { ArticleDigestFeed, - EmptyArticle, + EmptyFollowingTag, InfiniteScroll, List, Spinner, @@ -94,7 +94,7 @@ const TagsArticles = ({ tagIds }: { tagIds: string[] }) => { data?.viewer?.recommendation.followingTagsArticles || {} if (!edges || edges.length <= 0 || !pageInfo) { - return + return } const loadMore = () => { @@ -183,7 +183,7 @@ const TagsFeed = () => { const { edges } = data?.viewer?.recommendation.followingTags || {} if (!edges || edges.length <= 0) { - return + return } const tagIds = edges.map(({ node }) => node.id) From 795fdd7a6c02854087538d3200df78346a88b0f0 Mon Sep 17 00:00:00 2001 From: RoberTu Date: Sat, 11 Jul 2020 00:26:35 +0800 Subject: [PATCH 21/86] fix(infinite-scroll): fix incorrect waypoint offset makes loadMore was called on page load --- package-lock.json | 2 +- src/components/Interaction/InfiniteScroll.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 13fb948c37..a415b1a829 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "matters-web", - "version": "3.10.0", + "version": "3.10.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/src/components/Interaction/InfiniteScroll.tsx b/src/components/Interaction/InfiniteScroll.tsx index 9b2edaec03..9a639c60ac 100644 --- a/src/components/Interaction/InfiniteScroll.tsx +++ b/src/components/Interaction/InfiniteScroll.tsx @@ -62,7 +62,7 @@ export const InfiniteScroll: React.FC = ({
{children} {hasNextPage && ( - loadMore()}> + )} From f83375a0624b87626fe016786a96bccf273e0c53 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sun, 12 Jul 2020 21:16:34 +0000 Subject: [PATCH 22/86] build(deps-dev): bump @types/react from 16.9.41 to 16.9.43 Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 16.9.41 to 16.9.43. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react) Signed-off-by: dependabot-preview[bot] --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index a415b1a829..7a0ba47216 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6010,9 +6010,9 @@ "dev": true }, "@types/react": { - "version": "16.9.41", - "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.41.tgz", - "integrity": "sha512-6cFei7F7L4wwuM+IND/Q2cV1koQUvJ8iSV+Gwn0c3kvABZ691g7sp3hfEQHOUBJtccl1gPi+EyNjMIl9nGA0ug==", + "version": "16.9.43", + "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.43.tgz", + "integrity": "sha512-PxshAFcnJqIWYpJbLPriClH53Z2WlJcVZE+NP2etUtWQs2s7yIMj3/LDKZT/5CHJ/F62iyjVCDu2H3jHEXIxSg==", "dev": true, "requires": { "@types/prop-types": "*", diff --git a/package.json b/package.json index 6a96b16f5a..97eed19b95 100644 --- a/package.json +++ b/package.json @@ -101,7 +101,7 @@ "@types/lodash": "^4.14.157", "@types/nprogress": "0.2.0", "@types/pulltorefreshjs": "^0.1.3", - "@types/react": "^16.9.41", + "@types/react": "^16.9.43", "@types/react-beautiful-dnd": "^13.0.0", "@types/react-copy-to-clipboard": "^4.3.0", "@types/react-dom": "^16.9.8", From a72d5a53c803d101996c7feba7580cfebc21920f Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sun, 12 Jul 2020 21:19:11 +0000 Subject: [PATCH 23/86] build(deps-dev): bump apollo from 2.28.3 to 2.29.1 Bumps [apollo](https://github.com/apollographql/apollo-tooling) from 2.28.3 to 2.29.1. - [Release notes](https://github.com/apollographql/apollo-tooling/releases) - [Changelog](https://github.com/apollographql/apollo-tooling/blob/master/CHANGELOG.md) - [Commits](https://github.com/apollographql/apollo-tooling/compare/apollo@2.28.3...apollo@2.29.1) Signed-off-by: dependabot-preview[bot] --- package-lock.json | 1058 +++++++++++++++++++++++++++++++-------------- package.json | 2 +- 2 files changed, 725 insertions(+), 335 deletions(-) diff --git a/package-lock.json b/package-lock.json index a415b1a829..af4f2c342f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -58,17 +58,27 @@ } }, "@apollo/federation": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/@apollo/federation/-/federation-0.16.2.tgz", - "integrity": "sha512-pjTkcl1KGxLZOPpVyTygZNuLxZJCCMvGVonPJMoFzQYt63/o0DwpwbcNlbvpdryWjjFgvi5diqJxRFGuxndEPA==", + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@apollo/federation/-/federation-0.17.0.tgz", + "integrity": "sha512-vSW/M8+SGdu5xALsA/RL37GgB+wNFZpXCyPAcg3b68c8x7uoQHgYwqwUu7D+GnAGeOpDUrNnFPdKAYW7elYkyQ==", "dev": true, "requires": { "apollo-graphql": "^0.4.0", - "apollo-server-env": "^2.4.4", + "apollo-server-env": "^2.4.5", "core-js": "^3.4.0", "lodash.xorby": "^4.7.0" }, "dependencies": { + "apollo-graphql": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/apollo-graphql/-/apollo-graphql-0.4.5.tgz", + "integrity": "sha512-0qa7UOoq7E71kBYE7idi6mNQhHLVdMEDInWk6TNw3KsSWZE2/I68gARP84Mj+paFTO5NYuw1Dht66PVX76Cc2w==", + "dev": true, + "requires": { + "apollo-env": "^0.6.5", + "lodash.sortby": "^4.7.0" + } + }, "core-js": { "version": "3.6.5", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz", @@ -1727,9 +1737,9 @@ "dev": true }, "@endemolshinegroup/cosmiconfig-typescript-loader": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@endemolshinegroup/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-1.0.1.tgz", - "integrity": "sha512-bhUR9035PbgL6A/nfLayjoqKo4W7hCtzxqVxq2cgDB+Ndpsa3dGIr71/ymgY3vCTCQaufkFxAcEeoECyJ498CA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@endemolshinegroup/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-1.0.2.tgz", + "integrity": "sha512-ZHkXKq2XFFmAUdmSZrmqUSIrRM4O9gtkdpxMmV+LQl7kScUnbo6pMnXu6+FTDgZ12aW6SDoZoOJfS56WD+Eu6A==", "dev": true, "requires": { "lodash.get": "^4", @@ -3436,13 +3446,13 @@ } }, "@oclif/command": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@oclif/command/-/command-1.6.1.tgz", - "integrity": "sha512-pvmMmfGn+zm4e4RwVw63mg9sIaqKqmVsFbImQoUrCO/43UmWzoSHWNXKdgEGigOezWrkZfFucaeZcSbp149OWg==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@oclif/command/-/command-1.7.0.tgz", + "integrity": "sha512-TkknFtWcZI8te0E8sW+ohiblExrLx73rIcV4KdIzDX01u+oTZWZaap51F6TSGFnR/Gey0WctaDvJhZlt4xgKdA==", "dev": true, "requires": { "@oclif/config": "^1.15.1", - "@oclif/errors": "^1.2.2", + "@oclif/errors": "^1.3.3", "@oclif/parser": "^3.8.3", "@oclif/plugin-help": "^3", "debug": "^4.1.1", @@ -3512,18 +3522,58 @@ "requires": { "ansi-regex": "^4.1.0" } + }, + "wrap-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-4.0.0.tgz", + "integrity": "sha512-uMTsj9rDb0/7kk1PbcbCcwvHUxp60fGDB/NNXpVa0Q+ic/e7y5+BwTxKfQ33VYgDppSwi/FBzpetYzo8s6tfbg==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } } } }, "@oclif/config": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/@oclif/config/-/config-1.15.1.tgz", - "integrity": "sha512-GdyHpEZuWlfU8GSaZoiywtfVBsPcfYn1KuSLT1JTfvZGpPG6vShcGr24YZ3HG2jXUFlIuAqDcYlTzOrqOdTPNQ==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/@oclif/config/-/config-1.16.0.tgz", + "integrity": "sha512-vOnMPQcHokC03WBCuLipTxksTwgZcmDOnH2H0UHqndfKKN9GVDzpZTH6zaFVQBdjTME5VtRzg9A2UaNmq6OXWw==", "dev": true, "requires": { - "@oclif/errors": "^1.0.0", + "@oclif/errors": "^1.3.3", "@oclif/parser": "^3.8.0", "debug": "^4.1.1", + "globby": "^11.0.1", + "is-wsl": "^2.1.1", "tslib": "^1.9.3" }, "dependencies": { @@ -3536,6 +3586,29 @@ "ms": "^2.1.1" } }, + "globby": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.1.tgz", + "integrity": "sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + } + }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "requires": { + "is-docker": "^2.0.0" + } + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -3545,44 +3618,32 @@ } }, "@oclif/errors": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@oclif/errors/-/errors-1.2.2.tgz", - "integrity": "sha512-Eq8BFuJUQcbAPVofDxwdE0bL14inIiwt5EaKRVY9ZDIG11jwdXZqiQEECJx0VfnLyUZdYfRd/znDI/MytdJoKg==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@oclif/errors/-/errors-1.3.3.tgz", + "integrity": "sha512-EJR6AIOEkt/NnARNIVAskPDVtdhtO5TTNXmhDrGqMoWVsr0R6DkkLrMyq95BmHvlVWM1nduoq4fQPuCyuF2jaA==", "dev": true, "requires": { - "clean-stack": "^1.3.0", - "fs-extra": "^7.0.0", - "indent-string": "^3.2.0", - "strip-ansi": "^5.0.0", - "wrap-ansi": "^4.0.0" + "clean-stack": "^3.0.0", + "fs-extra": "^9.0.1", + "indent-string": "^4.0.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" }, "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, "clean-stack": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-1.3.0.tgz", - "integrity": "sha1-noIVAa6XmYbEax1m0tQy2y/UrjE=", - "dev": true - }, - "indent-string": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", - "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", - "dev": true - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-3.0.0.tgz", + "integrity": "sha512-RHxtgFvXsRQ+1AM7dlozLDY7ssmvUUh0XEnfnyhYgJTO6beNZHBogiaCwGM9Q3rFrUkYxOtsZRC0zAturg5bjg==", "dev": true, "requires": { - "ansi-regex": "^4.1.0" + "escape-string-regexp": "4.0.0" } + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true } } }, @@ -3628,6 +3689,17 @@ "ms": "^2.1.1" } }, + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -3677,6 +3749,44 @@ "requires": { "ansi-regex": "^4.1.0" } + }, + "wrap-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-4.0.0.tgz", + "integrity": "sha512-uMTsj9rDb0/7kk1PbcbCcwvHUxp60fGDB/NNXpVa0Q+ic/e7y5+BwTxKfQ33VYgDppSwi/FBzpetYzo8s6tfbg==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } } } }, @@ -3734,6 +3844,23 @@ "tslib": "^1.9.3" } }, + "extract-stack": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/extract-stack/-/extract-stack-1.0.0.tgz", + "integrity": "sha1-uXrK+UQe6iMyUpYktzL8WhyBZfo=", + "dev": true + }, + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, "indent-string": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", @@ -3767,12 +3894,12 @@ } }, "@oclif/plugin-plugins": { - "version": "1.7.9", - "resolved": "https://registry.npmjs.org/@oclif/plugin-plugins/-/plugin-plugins-1.7.9.tgz", - "integrity": "sha512-o7qfmiUGl+NUyA2lM18/Ch5sasGGYPIINR3cZ/AjwtdQ3ooINnF00pUDcUOtbjW97gRmk6/j79tcyTo8i7rHZg==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@oclif/plugin-plugins/-/plugin-plugins-1.9.0.tgz", + "integrity": "sha512-sq31nJk/n5pH5qGDioj2Z9x6MlRUrc/kkQrfCYKRPbQM80qewSP4RcPK3/gDvDSOAWD3wLAK9oMbDQO9lqImMA==", "dev": true, "requires": { - "@oclif/color": "^0.0.0", + "@oclif/color": "^0.x", "@oclif/command": "^1.5.12", "chalk": "^2.4.2", "cli-ux": "^5.2.1", @@ -3781,22 +3908,11 @@ "http-call": "^5.2.2", "load-json-file": "^5.2.0", "npm-run-path": "^3.0.0", - "semver": "^5.6.0", - "tslib": "^1.9.3", + "semver": "^7.3.2", + "tslib": "^2.0.0", "yarn": "^1.21.1" }, "dependencies": { - "@oclif/color": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/@oclif/color/-/color-0.0.0.tgz", - "integrity": "sha512-KKd3W7eNwfNF061tr663oUNdt8EMnfuyf5Xv55SGWA1a0rjhWqS/32P7OeB7CbXcJUBdfVrPyR//1afaW12AWw==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "supports-color": "^5.4.0", - "tslib": "^1" - } - }, "debug": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", @@ -3806,6 +3922,17 @@ "ms": "^2.1.1" } }, + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -3813,19 +3940,16 @@ "dev": true }, "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", "dev": true }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } + "tslib": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.0.tgz", + "integrity": "sha512-lTqkx847PI7xEDYJntxZH89L2/aXInsyF2luSafe/+0fHOMjlBNXdH6th7f70qxLDhul7KZK0zC8V5ZIyHl0/g==", + "dev": true } } }, @@ -3855,6 +3979,17 @@ "ms": "^2.1.1" } }, + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -6567,43 +6702,43 @@ } }, "apollo": { - "version": "2.28.3", - "resolved": "https://registry.npmjs.org/apollo/-/apollo-2.28.3.tgz", - "integrity": "sha512-+X1RqODYOz1VPO0a/6tZpZiFQr7K6z0ZSm7H9oT9PmuZ9TMC27mwOh2N0i1p+OP+6JORKh8lHmjMvle+doZ82A==", + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/apollo/-/apollo-2.29.1.tgz", + "integrity": "sha512-2wobALKlgjQsSPEjP4jN+ceH7DnZdOkcY28owNP6v1hdPhsznOdc0AYYvR9dMlb+NzcBqTOlqJekFUkzfM00Ig==", "dev": true, "requires": { "@apollographql/apollo-tools": "^0.4.8", - "@oclif/command": "1.6.1", - "@oclif/config": "1.15.1", - "@oclif/errors": "1.2.2", + "@oclif/command": "1.7.0", + "@oclif/config": "1.16.0", + "@oclif/errors": "1.3.3", "@oclif/plugin-autocomplete": "0.2.0", "@oclif/plugin-help": "2.2.3", "@oclif/plugin-not-found": "1.2.4", - "@oclif/plugin-plugins": "1.7.9", + "@oclif/plugin-plugins": "1.9.0", "@oclif/plugin-warn-if-update-available": "1.7.0", - "apollo-codegen-core": "^0.37.3", - "apollo-codegen-flow": "^0.35.3", - "apollo-codegen-scala": "^0.36.3", - "apollo-codegen-swift": "^0.37.3", - "apollo-codegen-typescript": "^0.37.3", + "apollo-codegen-core": "^0.37.5", + "apollo-codegen-flow": "^0.35.5", + "apollo-codegen-scala": "^0.36.5", + "apollo-codegen-swift": "^0.37.5", + "apollo-codegen-typescript": "^0.37.5", "apollo-env": "^0.6.5", - "apollo-graphql": "^0.4.5", - "apollo-language-server": "^1.22.3", + "apollo-graphql": "^0.5.0", + "apollo-language-server": "^1.23.1", "chalk": "2.4.2", - "cli-ux": "5.4.6", + "cli-ux": "5.4.9", "env-ci": "3.2.2", "gaze": "1.1.3", "git-parse": "1.0.4", "git-rev-sync": "2.0.0", "git-url-parse": "^11.1.2", "glob": "7.1.5", - "graphql": "14.0.2 - 14.2.0 || ^14.3.1", + "graphql": "14.0.2 - 14.2.0 || ^14.3.1 || ^15.0.0", "graphql-tag": "2.10.3", "listr": "0.14.3", "lodash.identity": "3.0.0", "lodash.pickby": "4.6.0", "mkdirp": "0.5.5", - "moment": "2.26.0", + "moment": "2.27.0", "strip-ansi": "5.2.0", "table": "5.4.6", "tty": "1.0.1", @@ -6698,40 +6833,46 @@ } }, "apollo-codegen-core": { - "version": "0.37.3", - "resolved": "https://registry.npmjs.org/apollo-codegen-core/-/apollo-codegen-core-0.37.3.tgz", - "integrity": "sha512-/DwAhOOFzl57GdBfRGNnqIAcfZAXpsgFIeWYqlu3I/eIucGBCFWo9CEW1TcNwkZzYGAmSE8tURwPgt7dtnhFpg==", + "version": "0.37.5", + "resolved": "https://registry.npmjs.org/apollo-codegen-core/-/apollo-codegen-core-0.37.5.tgz", + "integrity": "sha512-mCfizUDuYsHQlZOwizc5Z2YRlvTakvQDjpDDQUAHW2ULMaCAnRSFEo+ksN+e1VdR4fAOZ1cEtCmIZ+HqpL2Bpw==", "dev": true, "requires": { - "@babel/generator": "7.10.2", + "@babel/generator": "7.10.4", "@babel/parser": "^7.1.3", - "@babel/types": "7.10.2", + "@babel/types": "7.10.4", "apollo-env": "^0.6.5", - "apollo-language-server": "^1.22.3", + "apollo-language-server": "^1.23.1", "ast-types": "^0.13.0", "common-tags": "^1.5.1", "recast": "^0.19.0" }, "dependencies": { "@babel/generator": { - "version": "7.10.2", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.10.2.tgz", - "integrity": "sha512-AxfBNHNu99DTMvlUPlt1h2+Hn7knPpH5ayJ8OqDWSeLld+Fi2AYBTC/IejWDM9Edcii4UzZRCsbUt0WlSDsDsA==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.10.4.tgz", + "integrity": "sha512-toLIHUIAgcQygFZRAQcsLQV3CBuX6yOIru1kJk/qqqvcRmZrYe6WavZTSG+bB8MxhnL9YPf+pKQfuiP161q7ng==", "dev": true, "requires": { - "@babel/types": "^7.10.2", + "@babel/types": "^7.10.4", "jsesc": "^2.5.1", "lodash": "^4.17.13", "source-map": "^0.5.0" } }, + "@babel/helper-validator-identifier": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", + "dev": true + }, "@babel/types": { - "version": "7.10.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.2.tgz", - "integrity": "sha512-AD3AwWBSz0AWF0AkCN9VPiWrvldXq+/e3cHa4J89vo4ymjz1XwrBFFVZmkJTsQIPNk+ZVomPSXUJqq8yyjZsng==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.4.tgz", + "integrity": "sha512-UTCFOxC3FsFHb7lkRMVvgLzaRVamXuAs2Tz4wajva4WxtVY82eZeaUBtC2Zt95FU9TiznuC0Zk35tsim8jeVpg==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.10.1", + "@babel/helper-validator-identifier": "^7.10.4", "lodash": "^4.17.13", "to-fast-properties": "^2.0.0" } @@ -6745,38 +6886,44 @@ } }, "apollo-codegen-flow": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/apollo-codegen-flow/-/apollo-codegen-flow-0.35.3.tgz", - "integrity": "sha512-npDt9PEJiw/ygKUsKxaDGHCnjQUANPlg/F9piIbQ71jwBjNNoNSXrRaRiD5632MfcTvEuBvanElEX3AO2sA1gw==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/apollo-codegen-flow/-/apollo-codegen-flow-0.35.5.tgz", + "integrity": "sha512-1/fPPehm758yFuZIcMWKbYjJtapa6Pwuqo0broYKBSQ9lp42LSvbCfwtANktBnmxrFmlIFPFrBOnsjrz2eW+5w==", "dev": true, "requires": { - "@babel/generator": "7.10.2", - "@babel/types": "7.10.2", - "apollo-codegen-core": "^0.37.3", - "change-case": "^3.0.1", + "@babel/generator": "7.10.4", + "@babel/types": "7.10.4", + "apollo-codegen-core": "^0.37.5", + "change-case": "^4.0.0", "common-tags": "^1.5.1", "inflected": "^2.0.3" }, "dependencies": { "@babel/generator": { - "version": "7.10.2", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.10.2.tgz", - "integrity": "sha512-AxfBNHNu99DTMvlUPlt1h2+Hn7knPpH5ayJ8OqDWSeLld+Fi2AYBTC/IejWDM9Edcii4UzZRCsbUt0WlSDsDsA==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.10.4.tgz", + "integrity": "sha512-toLIHUIAgcQygFZRAQcsLQV3CBuX6yOIru1kJk/qqqvcRmZrYe6WavZTSG+bB8MxhnL9YPf+pKQfuiP161q7ng==", "dev": true, "requires": { - "@babel/types": "^7.10.2", + "@babel/types": "^7.10.4", "jsesc": "^2.5.1", "lodash": "^4.17.13", "source-map": "^0.5.0" } }, + "@babel/helper-validator-identifier": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", + "dev": true + }, "@babel/types": { - "version": "7.10.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.2.tgz", - "integrity": "sha512-AD3AwWBSz0AWF0AkCN9VPiWrvldXq+/e3cHa4J89vo4ymjz1XwrBFFVZmkJTsQIPNk+ZVomPSXUJqq8yyjZsng==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.4.tgz", + "integrity": "sha512-UTCFOxC3FsFHb7lkRMVvgLzaRVamXuAs2Tz4wajva4WxtVY82eZeaUBtC2Zt95FU9TiznuC0Zk35tsim8jeVpg==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.10.1", + "@babel/helper-validator-identifier": "^7.10.4", "lodash": "^4.17.13", "to-fast-properties": "^2.0.0" } @@ -6790,62 +6937,68 @@ } }, "apollo-codegen-scala": { - "version": "0.36.3", - "resolved": "https://registry.npmjs.org/apollo-codegen-scala/-/apollo-codegen-scala-0.36.3.tgz", - "integrity": "sha512-KOmcP/0RiqSGY03fXdF87IzQ1RSsApbno3/M8KRVF2yhf+4X3GQXQRpXejHMt4DxnnfPBOn7MqwtP/cDMJIiyw==", + "version": "0.36.5", + "resolved": "https://registry.npmjs.org/apollo-codegen-scala/-/apollo-codegen-scala-0.36.5.tgz", + "integrity": "sha512-QCtD1rV5OtYjlxiLJ7rfrRxALNRYUwYdlxXiyqfZrIRqtpGVHwPp/nDqFF0bYUIT135vj7dXz7xzzlvK5x+z6A==", "dev": true, "requires": { - "apollo-codegen-core": "^0.37.3", - "change-case": "^3.0.1", + "apollo-codegen-core": "^0.37.5", + "change-case": "^4.0.0", "common-tags": "^1.5.1", "inflected": "^2.0.3" } }, "apollo-codegen-swift": { - "version": "0.37.3", - "resolved": "https://registry.npmjs.org/apollo-codegen-swift/-/apollo-codegen-swift-0.37.3.tgz", - "integrity": "sha512-0BpwtSE+IP12C7OdoScsOWUgxbcuThgwVRsxybBNugUcmddF5sCdGiRbdm+oKvX1vErsG+XimwmIuZnBPWv6ew==", + "version": "0.37.5", + "resolved": "https://registry.npmjs.org/apollo-codegen-swift/-/apollo-codegen-swift-0.37.5.tgz", + "integrity": "sha512-5WdO4IEA/tvEhnbBbbMYujUT/L65Dy3LNS88s/Wa5MkgG0g603wRgFsFOETFXiBiPIVguCszKIqFcK/uGBfsYQ==", "dev": true, "requires": { - "apollo-codegen-core": "^0.37.3", - "change-case": "^3.0.1", + "apollo-codegen-core": "^0.37.5", + "change-case": "^4.0.0", "common-tags": "^1.5.1", "inflected": "^2.0.3" } }, "apollo-codegen-typescript": { - "version": "0.37.3", - "resolved": "https://registry.npmjs.org/apollo-codegen-typescript/-/apollo-codegen-typescript-0.37.3.tgz", - "integrity": "sha512-tuf/AQTFcNrngQrT4q4WKRdiuPbglyR1m3L58g/nNevoO0cRmF6koIix4NB5sO05LgF8XJmd2zHvInUI5v33Ig==", + "version": "0.37.5", + "resolved": "https://registry.npmjs.org/apollo-codegen-typescript/-/apollo-codegen-typescript-0.37.5.tgz", + "integrity": "sha512-vfyr2P1ywUedepc410TwaX/+4M4KT/GOBWWL+fbeicWnxlecsDz5BMbD94nahRsvP8Iqd6ZIp3oxq4UeCSZbHQ==", "dev": true, "requires": { - "@babel/generator": "7.10.2", - "@babel/types": "7.10.2", - "apollo-codegen-core": "^0.37.3", - "change-case": "^3.0.1", + "@babel/generator": "7.10.4", + "@babel/types": "7.10.4", + "apollo-codegen-core": "^0.37.5", + "change-case": "^4.0.0", "common-tags": "^1.5.1", "inflected": "^2.0.3" }, "dependencies": { "@babel/generator": { - "version": "7.10.2", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.10.2.tgz", - "integrity": "sha512-AxfBNHNu99DTMvlUPlt1h2+Hn7knPpH5ayJ8OqDWSeLld+Fi2AYBTC/IejWDM9Edcii4UzZRCsbUt0WlSDsDsA==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.10.4.tgz", + "integrity": "sha512-toLIHUIAgcQygFZRAQcsLQV3CBuX6yOIru1kJk/qqqvcRmZrYe6WavZTSG+bB8MxhnL9YPf+pKQfuiP161q7ng==", "dev": true, "requires": { - "@babel/types": "^7.10.2", + "@babel/types": "^7.10.4", "jsesc": "^2.5.1", "lodash": "^4.17.13", "source-map": "^0.5.0" } }, + "@babel/helper-validator-identifier": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", + "dev": true + }, "@babel/types": { - "version": "7.10.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.2.tgz", - "integrity": "sha512-AD3AwWBSz0AWF0AkCN9VPiWrvldXq+/e3cHa4J89vo4ymjz1XwrBFFVZmkJTsQIPNk+ZVomPSXUJqq8yyjZsng==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.4.tgz", + "integrity": "sha512-UTCFOxC3FsFHb7lkRMVvgLzaRVamXuAs2Tz4wajva4WxtVY82eZeaUBtC2Zt95FU9TiznuC0Zk35tsim8jeVpg==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.10.1", + "@babel/helper-validator-identifier": "^7.10.4", "lodash": "^4.17.13", "to-fast-properties": "^2.0.0" } @@ -6859,13 +7012,13 @@ } }, "apollo-datasource": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/apollo-datasource/-/apollo-datasource-0.7.1.tgz", - "integrity": "sha512-h++/jQAY7GA+4TBM+7ezvctFmmGNLrAPf51KsagZj+NkT9qvxp585rdsuatynVbSl59toPK2EuVmc6ilmQHf+g==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/apollo-datasource/-/apollo-datasource-0.7.2.tgz", + "integrity": "sha512-ibnW+s4BMp4K2AgzLEtvzkjg7dJgCaw9M5b5N0YKNmeRZRnl/I/qBTQae648FsRKgMwTbRQIvBhQ0URUFAqFOw==", "dev": true, "requires": { - "apollo-server-caching": "^0.5.1", - "apollo-server-env": "^2.4.4" + "apollo-server-caching": "^0.5.2", + "apollo-server-env": "^2.4.5" } }, "apollo-env": { @@ -6895,9 +7048,9 @@ } }, "apollo-graphql": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/apollo-graphql/-/apollo-graphql-0.4.5.tgz", - "integrity": "sha512-0qa7UOoq7E71kBYE7idi6mNQhHLVdMEDInWk6TNw3KsSWZE2/I68gARP84Mj+paFTO5NYuw1Dht66PVX76Cc2w==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/apollo-graphql/-/apollo-graphql-0.5.0.tgz", + "integrity": "sha512-YSdF/BKPbsnQpxWpmCE53pBJX44aaoif31Y22I/qKpB6ZSGzYijV5YBoCL5Q15H2oA/v/02Oazh9lbp4ek3eig==", "dev": true, "requires": { "apollo-env": "^0.6.5", @@ -6905,18 +7058,18 @@ } }, "apollo-language-server": { - "version": "1.22.3", - "resolved": "https://registry.npmjs.org/apollo-language-server/-/apollo-language-server-1.22.3.tgz", - "integrity": "sha512-RurKlBUNE1RrvY4m93b5WS/DXInUEI47MlzuvholRqZSQovt2rQi81R0RvmS/l3d6y5TBfxbPFpT5RyHbdAntw==", + "version": "1.23.1", + "resolved": "https://registry.npmjs.org/apollo-language-server/-/apollo-language-server-1.23.1.tgz", + "integrity": "sha512-tP+B4jvs5KO/l1HYy4V6S302ypbmkyyDaWheQzlu0KStO9hlECDWk67nInmvKbRQ1Rov9oHDkujLADvH3pJkBA==", "dev": true, "requires": { - "@apollo/federation": "0.16.2", + "@apollo/federation": "0.17.0", "@apollographql/apollo-tools": "^0.4.8", "@apollographql/graphql-language-service-interface": "^2.0.2", "@endemolshinegroup/cosmiconfig-typescript-loader": "^1.0.0", "apollo-datasource": "^0.7.0", "apollo-env": "^0.6.5", - "apollo-graphql": "^0.4.5", + "apollo-graphql": "^0.5.0", "apollo-link": "^1.2.3", "apollo-link-context": "^1.0.9", "apollo-link-error": "^1.1.1", @@ -6927,12 +7080,12 @@ "cosmiconfig": "^5.0.6", "dotenv": "^8.0.0", "glob": "^7.1.3", - "graphql": "14.0.2 - 14.2.0 || ^14.3.1", + "graphql": "14.0.2 - 14.2.0 || ^14.3.1 || ^15.0.0", "graphql-tag": "^2.10.1", "lodash.debounce": "^4.0.8", "lodash.merge": "^4.6.1", "minimatch": "^3.0.4", - "moment": "2.26.0", + "moment": "2.27.0", "vscode-languageserver": "^5.1.0", "vscode-uri": "1.0.6" }, @@ -7015,18 +7168,18 @@ } }, "apollo-server-caching": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/apollo-server-caching/-/apollo-server-caching-0.5.1.tgz", - "integrity": "sha512-L7LHZ3k9Ao5OSf2WStvQhxdsNVplRQi7kCAPfqf9Z3GBEnQ2uaL0EgO0hSmtVHfXTbk5CTRziMT1Pe87bXrFIw==", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/apollo-server-caching/-/apollo-server-caching-0.5.2.tgz", + "integrity": "sha512-HUcP3TlgRsuGgeTOn8QMbkdx0hLPXyEJehZIPrcof0ATz7j7aTPA4at7gaiFHCo8gk07DaWYGB3PFgjboXRcWQ==", "dev": true, "requires": { "lru-cache": "^5.0.0" } }, "apollo-server-env": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/apollo-server-env/-/apollo-server-env-2.4.4.tgz", - "integrity": "sha512-c2oddDS3lwAl6QNCIKCLEzt/dF9M3/tjjYRVdxOVN20TidybI7rAbnT4QOzf4tORnGXtiznEAvr/Kc9ahhKADg==", + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/apollo-server-env/-/apollo-server-env-2.4.5.tgz", + "integrity": "sha512-nfNhmGPzbq3xCEWT8eRpoHXIPNcNy3QcEoBlzVMjeglrBGryLG2LXwBSPnVmTRRrzUYugX0ULBtgE3rBFNoUgA==", "dev": true, "requires": { "node-fetch": "^2.1.2", @@ -7042,9 +7195,9 @@ } }, "apollo-server-errors": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/apollo-server-errors/-/apollo-server-errors-2.4.1.tgz", - "integrity": "sha512-7oEd6pUxqyWYUbQ9TA8tM0NU/3aGtXSEibo6+txUkuHe7QaxfZ2wHRp+pfT1LC1K3RXYjKj61/C2xEO19s3Kdg==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/apollo-server-errors/-/apollo-server-errors-2.4.2.tgz", + "integrity": "sha512-FeGxW3Batn6sUtX3OVVUm7o56EgjxDlmgpTLNyWcLb0j6P8mw9oLNyAm3B+deHA4KNdNHO5BmHS2g1SJYjqPCQ==", "dev": true }, "apollo-utilities": { @@ -7286,6 +7439,12 @@ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", "dev": true }, + "at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true + }, "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", @@ -8525,13 +8684,13 @@ "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=" }, "camel-case": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", - "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.1.tgz", + "integrity": "sha512-7fa2WcG4fYFkclIvEmxBbTvmibwF2/agfEBc6q3lOpVu0A13ltLsA+Hr/8Hp6kp5f+G7hKi6t8lys6XxP+1K6Q==", "dev": true, "requires": { - "no-case": "^2.2.0", - "upper-case": "^1.1.1" + "pascal-case": "^3.1.1", + "tslib": "^1.10.0" } }, "camelcase": { @@ -8584,6 +8743,38 @@ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001066.tgz", "integrity": "sha512-Gfj/WAastBtfxLws0RCh2sDbTK/8rJuSeZMecrSkNGYxPcv7EzblmDGfWQCFEQcSqYE2BRgQiJh8HOD07N5hIw==" }, + "capital-case": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/capital-case/-/capital-case-1.0.3.tgz", + "integrity": "sha512-OlUSJpUr7SY0uZFOxcwnDOU7/MpHlKTZx2mqnDYQFrDudXLFm0JJ9wr/l4csB+rh2Ug0OPuoSO53PqiZBqno9A==", + "dev": true, + "requires": { + "no-case": "^3.0.3", + "tslib": "^1.10.0", + "upper-case-first": "^2.0.1" + }, + "dependencies": { + "lower-case": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.1.tgz", + "integrity": "sha512-LiWgfDLLb1dwbFQZsSglpRj+1ctGnayXz3Uv0/WO8n558JycT5fg6zkNcnW0G68Nn0aEldTFeEfmjCfmqry/rQ==", + "dev": true, + "requires": { + "tslib": "^1.10.0" + } + }, + "no-case": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.3.tgz", + "integrity": "sha512-ehY/mVQCf9BL0gKfsJBvFJen+1V//U+0HQMPrWct40ixE4jnv0bfvxDbWtAHL9EcaPEOJHVVYKoQn1TlZUB8Tw==", + "dev": true, + "requires": { + "lower-case": "^2.0.1", + "tslib": "^1.10.0" + } + } + } + }, "capture-exit": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", @@ -8672,29 +8863,44 @@ } }, "change-case": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/change-case/-/change-case-3.1.0.tgz", - "integrity": "sha512-2AZp7uJZbYEzRPsFoa+ijKdvp9zsrnnt6+yFokfwEpeJm0xuJDVoxiRCAaTzyJND8GJkofo2IcKWaUZ/OECVzw==", - "dev": true, - "requires": { - "camel-case": "^3.0.0", - "constant-case": "^2.0.0", - "dot-case": "^2.1.0", - "header-case": "^1.0.0", - "is-lower-case": "^1.1.0", - "is-upper-case": "^1.1.0", - "lower-case": "^1.1.1", - "lower-case-first": "^1.0.0", - "no-case": "^2.3.2", - "param-case": "^2.1.0", - "pascal-case": "^2.0.0", - "path-case": "^2.1.0", - "sentence-case": "^2.1.0", - "snake-case": "^2.1.0", - "swap-case": "^1.1.0", - "title-case": "^2.1.0", - "upper-case": "^1.1.1", - "upper-case-first": "^1.1.0" + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-4.1.1.tgz", + "integrity": "sha512-qRlUWn/hXnX1R1LBDF/RelJLiqNjKjUqlmuBVSEIyye8kq49CXqkZWKmi8XeUAdDXWFOcGLUMZ+aHn3Q5lzUXw==", + "dev": true, + "requires": { + "camel-case": "^4.1.1", + "capital-case": "^1.0.3", + "constant-case": "^3.0.3", + "dot-case": "^3.0.3", + "header-case": "^2.0.3", + "no-case": "^3.0.3", + "param-case": "^3.0.3", + "pascal-case": "^3.1.1", + "path-case": "^3.0.3", + "sentence-case": "^3.0.3", + "snake-case": "^3.0.3", + "tslib": "^1.10.0" + }, + "dependencies": { + "lower-case": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.1.tgz", + "integrity": "sha512-LiWgfDLLb1dwbFQZsSglpRj+1ctGnayXz3Uv0/WO8n558JycT5fg6zkNcnW0G68Nn0aEldTFeEfmjCfmqry/rQ==", + "dev": true, + "requires": { + "tslib": "^1.10.0" + } + }, + "no-case": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.3.tgz", + "integrity": "sha512-ehY/mVQCf9BL0gKfsJBvFJen+1V//U+0HQMPrWct40ixE4jnv0bfvxDbWtAHL9EcaPEOJHVVYKoQn1TlZUB8Tw==", + "dev": true, + "requires": { + "lower-case": "^2.0.1", + "tslib": "^1.10.0" + } + } } }, "character-entities": { @@ -8968,9 +9174,9 @@ } }, "cli-ux": { - "version": "5.4.6", - "resolved": "https://registry.npmjs.org/cli-ux/-/cli-ux-5.4.6.tgz", - "integrity": "sha512-EeiS2TzEndRVknCqE+8Ri8g0bsP617a1nq6n+3Trwft1JCDzyUNlX2J1fl7fwTgRPWtmBmiF6xIyueL5YGs65g==", + "version": "5.4.9", + "resolved": "https://registry.npmjs.org/cli-ux/-/cli-ux-5.4.9.tgz", + "integrity": "sha512-4yCKJbFQqNQxf1v0E5T5aBJLt3SbW6dXc/R7zHp4ycdPMg9fAy5f2vhPsWgXEGCMQg+fgN0Sp7EYcZ1XGkFyUA==", "dev": true, "requires": { "@oclif/command": "^1.6.0", @@ -8980,33 +9186,27 @@ "ansi-escapes": "^4.3.0", "ansi-styles": "^4.2.0", "cardinal": "^2.1.1", - "chalk": "^2.4.1", - "clean-stack": "^2.0.0", + "chalk": "^3.0.0", + "clean-stack": "^3.0.0", "cli-progress": "^3.4.0", - "extract-stack": "^1.0.0", - "fs-extra": "^7.0.1", + "extract-stack": "^2.0.0", + "fs-extra": "^9.0.1", "hyperlinker": "^1.0.0", "indent-string": "^4.0.0", - "is-wsl": "^1.1.0", + "is-wsl": "^2.2.0", "js-yaml": "^3.13.1", "lodash": "^4.17.11", "natural-orderby": "^2.0.1", "object-treeify": "^1.1.4", "password-prompt": "^1.1.2", "semver": "^5.6.0", - "string-width": "^3.1.0", + "string-width": "^4.2.0", "strip-ansi": "^5.1.0", - "supports-color": "^5.5.0", - "supports-hyperlinks": "^1.0.1", - "tslib": "^1.9.3" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, + "supports-color": "^7.1.0", + "supports-hyperlinks": "^1.0.1", + "tslib": "^2.0.0" + }, + "dependencies": { "ansi-styles": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", @@ -9017,6 +9217,25 @@ "color-convert": "^2.0.1" } }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "clean-stack": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-3.0.0.tgz", + "integrity": "sha512-RHxtgFvXsRQ+1AM7dlozLDY7ssmvUUh0XEnfnyhYgJTO6beNZHBogiaCwGM9Q3rFrUkYxOtsZRC0zAturg5bjg==", + "dev": true, + "requires": { + "escape-string-regexp": "4.0.0" + } + }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -9032,6 +9251,39 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "requires": { + "is-docker": "^2.0.0" + } + }, "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", @@ -9039,14 +9291,25 @@ "dev": true }, "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", "dev": true, "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } } }, "strip-ansi": { @@ -9056,16 +9319,30 @@ "dev": true, "requires": { "ansi-regex": "^4.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + } } }, "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" } + }, + "tslib": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.0.tgz", + "integrity": "sha512-lTqkx847PI7xEDYJntxZH89L2/aXInsyF2luSafe/+0fHOMjlBNXdH6th7f70qxLDhul7KZK0zC8V5ZIyHl0/g==", + "dev": true } } }, @@ -9427,13 +9704,44 @@ "integrity": "sha512-2/uRVMdRypf5z/TW/ncD/66l75P5hH2vM/GR8Jf8HLc2xnfJtmina6F6du8+v4Z2vTrMo7jC+W1tmEEuuELgkQ==" }, "constant-case": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-2.0.0.tgz", - "integrity": "sha1-QXV2TTidP6nI7NKRhu1gBSQ7akY=", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-3.0.3.tgz", + "integrity": "sha512-FXtsSnnrFYpzDmvwDGQW+l8XK3GV1coLyBN0eBz16ZUzGaZcT2ANVCJmLeuw2GQgxKHQIe9e0w2dzkSfaRlUmA==", "dev": true, "requires": { - "snake-case": "^2.1.0", - "upper-case": "^1.1.1" + "no-case": "^3.0.3", + "tslib": "^1.10.0", + "upper-case": "^2.0.1" + }, + "dependencies": { + "lower-case": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.1.tgz", + "integrity": "sha512-LiWgfDLLb1dwbFQZsSglpRj+1ctGnayXz3Uv0/WO8n558JycT5fg6zkNcnW0G68Nn0aEldTFeEfmjCfmqry/rQ==", + "dev": true, + "requires": { + "tslib": "^1.10.0" + } + }, + "no-case": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.3.tgz", + "integrity": "sha512-ehY/mVQCf9BL0gKfsJBvFJen+1V//U+0HQMPrWct40ixE4jnv0bfvxDbWtAHL9EcaPEOJHVVYKoQn1TlZUB8Tw==", + "dev": true, + "requires": { + "lower-case": "^2.0.1", + "tslib": "^1.10.0" + } + }, + "upper-case": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-2.0.1.tgz", + "integrity": "sha512-laAsbea9SY5osxrv7S99vH9xAaJKrw5Qpdh4ENRLcaxipjKsiaBwiAsxfa8X5mObKNTQPsupSq0J/VIxsSJe3A==", + "dev": true, + "requires": { + "tslib": "^1.10.0" + } + } } }, "constants-browserify": { @@ -10987,12 +11295,34 @@ "integrity": "sha512-ZjI4zqTaxveH2/tTlzS1wFp+7ncxNZaIEWYg3lzZRHkKf5zPT/MnEG6WL0BhHMJUabkh8GeU5NL5j+rEUCb7Ug==" }, "dot-case": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-2.1.1.tgz", - "integrity": "sha1-NNzzf1Co6TwrO8qLt/uRVcfaO+4=", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.3.tgz", + "integrity": "sha512-7hwEmg6RiSQfm/GwPL4AAWXKy3YNNZA3oFv2Pdiey0mwkRCPZ9x6SZbkLcn8Ma5PYeVokzoD4Twv2n7LKp5WeA==", "dev": true, "requires": { - "no-case": "^2.2.0" + "no-case": "^3.0.3", + "tslib": "^1.10.0" + }, + "dependencies": { + "lower-case": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.1.tgz", + "integrity": "sha512-LiWgfDLLb1dwbFQZsSglpRj+1ctGnayXz3Uv0/WO8n558JycT5fg6zkNcnW0G68Nn0aEldTFeEfmjCfmqry/rQ==", + "dev": true, + "requires": { + "tslib": "^1.10.0" + } + }, + "no-case": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.3.tgz", + "integrity": "sha512-ehY/mVQCf9BL0gKfsJBvFJen+1V//U+0HQMPrWct40ixE4jnv0bfvxDbWtAHL9EcaPEOJHVVYKoQn1TlZUB8Tw==", + "dev": true, + "requires": { + "lower-case": "^2.0.1", + "tslib": "^1.10.0" + } + } } }, "dot-prop": { @@ -11838,9 +12168,9 @@ "integrity": "sha512-qRW6y9eKF0VbCyOoOEtFhzJ3uykAw8GKwQVXyAIqwocyEWW4m+v+evec34RwtUkkxxHh7NKBLJ6AnXM8W4dH5w==" }, "extract-stack": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/extract-stack/-/extract-stack-1.0.0.tgz", - "integrity": "sha1-uXrK+UQe6iMyUpYktzL8WhyBZfo=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/extract-stack/-/extract-stack-2.0.0.tgz", + "integrity": "sha512-AEo4zm+TenK7zQorGK1f9mJ8L14hnTDi2ZQPR+Mub1NX8zimka1mXpV5LpH8x9HoUmFSHZCfLHqWvp0Y4FxxzQ==", "dev": true }, "extract-zip": { @@ -12496,14 +12826,33 @@ "dev": true }, "fs-extra": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", - "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", + "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^1.0.0" + }, + "dependencies": { + "jsonfile": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz", + "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^1.0.0" + } + }, + "universalify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", + "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==", + "dev": true + } } }, "fs-minipass": { @@ -13118,13 +13467,13 @@ "optional": true }, "header-case": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/header-case/-/header-case-1.0.1.tgz", - "integrity": "sha1-lTWXMZfBRLCWE81l0xfvGZY70C0=", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/header-case/-/header-case-2.0.3.tgz", + "integrity": "sha512-LChe/V32mnUQnTwTxd3aAlNMk8ia9tjCDb/LjYtoMrdAPApxLB+azejUk5ERZIZdIqvinwv6BAUuFXH/tQPdZA==", "dev": true, "requires": { - "no-case": "^2.2.0", - "upper-case": "^1.1.3" + "capital-case": "^1.0.3", + "tslib": "^1.10.0" } }, "helmet": { @@ -14188,8 +14537,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.0.0.tgz", "integrity": "sha512-pJEdRugimx4fBMra5z2/5iRdZ63OhYV0vr0Dwm5+xtW4D1FvRkB8hamMIhnWfyJeDdyr/aa7BDyNbtG38VxgoQ==", - "dev": true, - "optional": true + "dev": true }, "is-extendable": { "version": "0.1.1", @@ -14251,15 +14599,6 @@ "integrity": "sha1-LhmX+m6RZuqsAkLarkQ0A+TvHZc=", "dev": true }, - "is-lower-case": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/is-lower-case/-/is-lower-case-1.1.3.tgz", - "integrity": "sha1-fhR75HaNxGbbO/shzGCzHmrWk5M=", - "dev": true, - "requires": { - "lower-case": "^1.1.0" - } - }, "is-module": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", @@ -14414,15 +14753,6 @@ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", "dev": true }, - "is-upper-case": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-upper-case/-/is-upper-case-1.1.2.tgz", - "integrity": "sha1-jQsfp+eTOh5YSDYA7H2WYcuvdW8=", - "dev": true, - "requires": { - "upper-case": "^1.1.0" - } - }, "is-url": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", @@ -17642,15 +17972,6 @@ "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", "dev": true }, - "lower-case-first": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/lower-case-first/-/lower-case-first-1.0.2.tgz", - "integrity": "sha1-5dp8JvKacHO+AtUrrJmA5ZIq36E=", - "dev": true, - "requires": { - "lower-case": "^1.1.2" - } - }, "lowercase-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", @@ -18465,9 +18786,9 @@ } }, "moment": { - "version": "2.26.0", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.26.0.tgz", - "integrity": "sha512-oIixUO+OamkUkwjhAVE18rAMfRJNsNe/Stid/gwHSOfHrOtw9EhAY2AHvdKZ/k/MggcYELFCJz/Sn2pL8b8JMw==", + "version": "2.27.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.27.0.tgz", + "integrity": "sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ==", "dev": true }, "move-concurrently": { @@ -19237,9 +19558,9 @@ "integrity": "sha1-NwrnUvvzfePqcKhhwju6iRVpGUk=" }, "object-treeify": { - "version": "1.1.25", - "resolved": "https://registry.npmjs.org/object-treeify/-/object-treeify-1.1.25.tgz", - "integrity": "sha512-6Abx0xlXDnYd50JkQefvoIly3jWOu8/PqH4lh8p2/aMFEx5TjsUGHt0H9NHfzt+pCwOhpPgNYofD8e2YywIXig==", + "version": "1.1.26", + "resolved": "https://registry.npmjs.org/object-treeify/-/object-treeify-1.1.26.tgz", + "integrity": "sha512-0WTfU7SGM8umY4YPpOg+oHXL66E6dPVCr+sMR6KitPmvg8CkVrHUUZYEFtx0+5Wb0HjFEsBwBYXyGRNeX7c/oQ==", "dev": true }, "object-visit": { @@ -19711,12 +20032,13 @@ } }, "param-case": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", - "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.3.tgz", + "integrity": "sha512-VWBVyimc1+QrzappRs7waeN2YmoZFCGXWASRYX1/rGHtXqEcrGEIDm+jqIwFa2fRXNgQEwrxaYuIrX0WcAguTA==", "dev": true, "requires": { - "no-case": "^2.2.0" + "dot-case": "^3.0.3", + "tslib": "^1.10.0" } }, "parchment": { @@ -19817,13 +20139,34 @@ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" }, "pascal-case": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-2.0.1.tgz", - "integrity": "sha1-LVeNNFX2YNpl7KGO+VtODekSdh4=", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.1.tgz", + "integrity": "sha512-XIeHKqIrsquVTQL2crjq3NfJUxmdLasn3TYOU0VBM+UX2a6ztAWBlJQBePLGY7VHW8+2dRadeIPK5+KImwTxQA==", "dev": true, "requires": { - "camel-case": "^3.0.0", - "upper-case-first": "^1.1.0" + "no-case": "^3.0.3", + "tslib": "^1.10.0" + }, + "dependencies": { + "lower-case": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.1.tgz", + "integrity": "sha512-LiWgfDLLb1dwbFQZsSglpRj+1ctGnayXz3Uv0/WO8n558JycT5fg6zkNcnW0G68Nn0aEldTFeEfmjCfmqry/rQ==", + "dev": true, + "requires": { + "tslib": "^1.10.0" + } + }, + "no-case": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.3.tgz", + "integrity": "sha512-ehY/mVQCf9BL0gKfsJBvFJen+1V//U+0HQMPrWct40ixE4jnv0bfvxDbWtAHL9EcaPEOJHVVYKoQn1TlZUB8Tw==", + "dev": true, + "requires": { + "lower-case": "^2.0.1", + "tslib": "^1.10.0" + } + } } }, "pascalcase": { @@ -19855,12 +20198,13 @@ "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==" }, "path-case": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/path-case/-/path-case-2.1.1.tgz", - "integrity": "sha1-lLgDfDctP+KQbkZbtF4l0ibo7qU=", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/path-case/-/path-case-3.0.3.tgz", + "integrity": "sha512-UMFU6UETFpCNWbIWNczshPrnK/7JAXBP2NYw80ojElbQ2+JYxdqWDBkvvqM93u4u6oLmuJ/tPOf2tM8KtXv4eg==", "dev": true, "requires": { - "no-case": "^2.2.0" + "dot-case": "^3.0.3", + "tslib": "^1.10.0" } }, "path-dirname": { @@ -23052,13 +23396,35 @@ } }, "sentence-case": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-2.1.1.tgz", - "integrity": "sha1-H24t2jnBaL+S0T+G1KkYkz9mftQ=", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-3.0.3.tgz", + "integrity": "sha512-ZPr4dgTcNkEfcGOMFQyDdJrTU9uQO1nb1cjf+nuzb6FxgMDgKddZOM29qEsB7jvsZSMruLRcL2KfM4ypKpa0LA==", "dev": true, "requires": { - "no-case": "^2.2.0", - "upper-case-first": "^1.1.2" + "no-case": "^3.0.3", + "tslib": "^1.10.0", + "upper-case-first": "^2.0.1" + }, + "dependencies": { + "lower-case": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.1.tgz", + "integrity": "sha512-LiWgfDLLb1dwbFQZsSglpRj+1ctGnayXz3Uv0/WO8n558JycT5fg6zkNcnW0G68Nn0aEldTFeEfmjCfmqry/rQ==", + "dev": true, + "requires": { + "tslib": "^1.10.0" + } + }, + "no-case": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.3.tgz", + "integrity": "sha512-ehY/mVQCf9BL0gKfsJBvFJen+1V//U+0HQMPrWct40ixE4jnv0bfvxDbWtAHL9EcaPEOJHVVYKoQn1TlZUB8Tw==", + "dev": true, + "requires": { + "lower-case": "^2.0.1", + "tslib": "^1.10.0" + } + } } }, "serialize-error": { @@ -23232,12 +23598,13 @@ "dev": true }, "snake-case": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-2.1.0.tgz", - "integrity": "sha1-Qb2xtz8w7GagTU4srRt2OH1NbZ8=", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.3.tgz", + "integrity": "sha512-WM1sIXEO+rsAHBKjGf/6R1HBBcgbncKS08d2Aqec/mrDSpU80SiOU41hO7ny6DToHSyrlwTYzQBIK1FPSx4Y3Q==", "dev": true, "requires": { - "no-case": "^2.2.0" + "dot-case": "^3.0.3", + "tslib": "^1.10.0" } }, "snapdragon": { @@ -24741,16 +25108,6 @@ "util.promisify": "~1.0.0" } }, - "swap-case": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/swap-case/-/swap-case-1.1.2.tgz", - "integrity": "sha1-w5IDpFhzhfrTyFCgvRvK+ggZdOM=", - "dev": true, - "requires": { - "lower-case": "^1.1.1", - "upper-case": "^1.1.1" - } - }, "symbol-observable": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", @@ -25863,12 +26220,12 @@ "dev": true }, "upper-case-first": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-1.1.2.tgz", - "integrity": "sha1-XXm+3P8UQZUY/S7bCgUHybaFkRU=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.1.tgz", + "integrity": "sha512-105J8XqQ+9RxW3l9gHZtgve5oaiR9TIwvmZAMAIZWRHe00T21cdvewKORTlOJf/zXW6VukuTshM+HXZNWz7N5w==", "dev": true, "requires": { - "upper-case": "^1.1.1" + "tslib": "^1.10.0" } }, "uri-js": { @@ -26880,29 +27237,62 @@ } }, "wrap-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-4.0.0.tgz", - "integrity": "sha512-uMTsj9rDb0/7kk1PbcbCcwvHUxp60fGDB/NNXpVa0Q+ic/e7y5+BwTxKfQ33VYgDppSwi/FBzpetYzo8s6tfbg==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "dependencies": { - "ansi-regex": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" } } } diff --git a/package.json b/package.json index 6a96b16f5a..2ca7d59699 100644 --- a/package.json +++ b/package.json @@ -111,7 +111,7 @@ "@types/styled-jsx": "^2.2.8", "@types/validator": "^13.1.0", "@zeit/next-bundle-analyzer": "^0.1.2", - "apollo": "^2.28.3", + "apollo": "^2.29.1", "babel-jest": "^26.1.0", "babel-plugin-dynamic-import-node": "^2.3.3", "babel-polyfill": "^6.26.0", From c54dbcdac777a1999a89c5a59c82e941519398f3 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sun, 12 Jul 2020 21:21:55 +0000 Subject: [PATCH 24/86] build(deps): bump subscriptions-transport-ws from 0.9.16 to 0.9.17 Bumps [subscriptions-transport-ws](https://github.com/apollostack/subscriptions-transport-ws) from 0.9.16 to 0.9.17. - [Release notes](https://github.com/apollostack/subscriptions-transport-ws/releases) - [Changelog](https://github.com/apollographql/subscriptions-transport-ws/blob/master/CHANGELOG.md) - [Commits](https://github.com/apollostack/subscriptions-transport-ws/commits) Signed-off-by: dependabot-preview[bot] --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index a415b1a829..090df4b3e5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24639,9 +24639,9 @@ "integrity": "sha512-nTbZoaqoBnmK+ptANthb10ZRZOGC+EmTLLUxeYIuHNkEKcmKgXX1XWKkUBT2Ac4es3NybooPe0SmvKdhKJZAuw==" }, "subscriptions-transport-ws": { - "version": "0.9.16", - "resolved": "https://registry.npmjs.org/subscriptions-transport-ws/-/subscriptions-transport-ws-0.9.16.tgz", - "integrity": "sha512-pQdoU7nC+EpStXnCfh/+ho0zE0Z+ma+i7xvj7bkXKb1dvYHSZxgRPaU6spRP+Bjzow67c/rRDoix5RT0uU9omw==", + "version": "0.9.17", + "resolved": "https://registry.npmjs.org/subscriptions-transport-ws/-/subscriptions-transport-ws-0.9.17.tgz", + "integrity": "sha512-hNHi2N80PBz4T0V0QhnnsMGvG3XDFDS9mS6BhZ3R12T6EBywC8d/uJscsga0cVO4DKtXCkCRrWm2sOYrbOdhEA==", "requires": { "backo2": "^1.0.2", "eventemitter3": "^3.1.0", diff --git a/package.json b/package.json index 6a96b16f5a..3f63b06814 100644 --- a/package.json +++ b/package.json @@ -82,7 +82,7 @@ "react-use-gesture": "^7.0.15", "react-virtualized": "^9.21.2", "react-waypoint": "^9.0.3", - "subscriptions-transport-ws": "^0.9.16", + "subscriptions-transport-ws": "^0.9.17", "use-debounce": "^3.4.3", "validator": "^13.1.1" }, From 83ceb33d9b4be823746ff9013a10a3cfc007a46a Mon Sep 17 00:00:00 2001 From: RoberTu Date: Mon, 13 Jul 2020 10:54:38 +0800 Subject: [PATCH 25/86] fix(public-private-queries): fetch content of archived article if viewer is the author --- src/views/ArticleDetail/gql.ts | 3 ++- src/views/ArticleDetail/index.tsx | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/views/ArticleDetail/gql.ts b/src/views/ArticleDetail/gql.ts index c6e19fd83f..53bc94cb6f 100644 --- a/src/views/ArticleDetail/gql.ts +++ b/src/views/ArticleDetail/gql.ts @@ -50,9 +50,10 @@ export const ARTICLE_DETAIL_PUBLIC = gql` ` export const ARTICLE_DETAIL_PRIVATE = gql` - query ArticleDetailPrivate($mediaHash: String) { + query ArticleDetailPrivate($mediaHash: String, $includeContent: Boolean!) { article(input: { mediaHash: $mediaHash }) { id + content @include(if: $includeContent) author { ...UserDigestRichUserPrivate } diff --git a/src/views/ArticleDetail/index.tsx b/src/views/ArticleDetail/index.tsx index 8d1e619ffd..882ae6d6ca 100644 --- a/src/views/ArticleDetail/index.tsx +++ b/src/views/ArticleDetail/index.tsx @@ -109,6 +109,7 @@ const ArticleDetail = () => { fetchPolicy: 'network-only', variables: { mediaHash, + includeContent: article.state !== 'active' && isAuthor, }, }) }, [mediaHash, viewer.id, article]) @@ -204,7 +205,7 @@ const ArticleDetail = () => { /** * Render:Archived/Banned */ - if (article.state !== 'active' && viewer.id !== authorId) { + if (article.state !== 'active' && !isAuthor) { return ( Date: Mon, 13 Jul 2020 13:52:51 +0800 Subject: [PATCH 26/86] fix(edit-article): fix tags merging on editing --- src/views/ArticleDetail/EditMode/Sidebar/index.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/views/ArticleDetail/EditMode/Sidebar/index.tsx b/src/views/ArticleDetail/EditMode/Sidebar/index.tsx index 5fee85f315..fb432ca576 100644 --- a/src/views/ArticleDetail/EditMode/Sidebar/index.tsx +++ b/src/views/ArticleDetail/EditMode/Sidebar/index.tsx @@ -76,8 +76,10 @@ const EditModeSidebar = ({ <> 0 ? editModeTags : tags} - onAddTag={(tag) => setEditModeTags(_uniq(tags.concat(tag)))} - onDeleteTag={(tag) => setEditModeTags(tags.filter((it) => it !== tag))} + onAddTag={(tag) => setEditModeTags(_uniq(editModeTags.concat(tag)))} + onDeleteTag={(tag) => + setEditModeTags(editModeTags.filter((it) => it !== tag)) + } /> Date: Mon, 13 Jul 2020 00:31:25 -0700 Subject: [PATCH 27/86] feat(analytics): add view dialog events for addCredit andd donation --- src/common/utils/analytics.ts | 5 +++ .../Dialogs/AddCreditDialog/index.tsx | 16 +++++++++- .../Dialogs/DonationDialog/index.tsx | 31 ++++++++++++++++--- 3 files changed, 47 insertions(+), 5 deletions(-) diff --git a/src/common/utils/analytics.ts b/src/common/utils/analytics.ts index 3c4061c03a..78ca76abd8 100644 --- a/src/common/utils/analytics.ts +++ b/src/common/utils/analytics.ts @@ -25,6 +25,7 @@ type EventArgs = | ['load_more', LoadMoreProp] | ['share', ShareProp] | ['purchase', PurchaseProp] + | ['view_add_credit_dialog', ViewDialogProp] type ClickFeedProp = | ArticleFeedProp @@ -70,6 +71,10 @@ interface PurchaseProp { message?: string } +interface ViewDialogProp { + step: string +} + interface ArticleFeedProp { type: ArticleFeedType diff --git a/src/components/Dialogs/AddCreditDialog/index.tsx b/src/components/Dialogs/AddCreditDialog/index.tsx index edb8a17b1b..beb1960bf3 100644 --- a/src/components/Dialogs/AddCreditDialog/index.tsx +++ b/src/components/Dialogs/AddCreditDialog/index.tsx @@ -1,4 +1,4 @@ -import { useContext, useState } from 'react' +import { useContext, useEffect, useState } from 'react' import { Dialog, PaymentForm, ViewerContext } from '~/components' @@ -38,6 +38,7 @@ const BaseAddCreditDialog = ({ children }: AddCreditDialogProps) => { transaction: undefined, client_secret: '', }) + const resetData = () => setData({ transaction: undefined, @@ -50,12 +51,25 @@ const BaseAddCreditDialog = ({ children }: AddCreditDialogProps) => { analytics.trackEvent('click_button', { type: 'checkout' }) } + // set password if needed const isSetPaymentPassword = step === 'setPaymentPassword' + + // confirm add credit amount const isConfirm = step === 'confirm' + + // stripe elements for credit card info const isCheckout = step === 'checkout' + + // loader and error catching const isProcessing = step === 'processing' + + // confirmation const isComplete = step === 'complete' + useEffect(() => { + analytics.trackEvent('view_add_credit_dialog', { step }) + }, [step]) + return ( <> {children({ open })} diff --git a/src/components/Dialogs/DonationDialog/index.tsx b/src/components/Dialogs/DonationDialog/index.tsx index 10a2764fb9..852231b137 100644 --- a/src/components/Dialogs/DonationDialog/index.tsx +++ b/src/components/Dialogs/DonationDialog/index.tsx @@ -1,10 +1,10 @@ import gql from 'graphql-tag' -import { useContext, useState } from 'react' +import { useContext, useEffect, useState } from 'react' import { Dialog, PaymentForm, Translate, ViewerContext } from '~/components' import { PAYMENT_CURRENCY as CURRENCY } from '~/common/enums' -import { numRound } from '~/common/utils' +import { analytics, numRound } from '~/common/utils' import { AddCredit_addCredit_transaction as AddCreditTx } from '~/components/Forms/PaymentForm/AddCredit/__generated__/AddCredit' import { PayTo_payTo_transaction as PayToTx } from '~/components/GQL/mutations/__generated__/PayTo' @@ -152,21 +152,44 @@ const BaseDonationDialog = ({ ) + /** + * Add Credit + */ + // add credit when credit not enough const isAddCredit = step === 'addCredit' const isAddCreditComplete = step === 'addCreditComplete' const isAddCreditProcessing = step === 'addCreditProcessing' + // stripe elements const isCheckout = step === 'checkout' + // processing + const isProcessing = step === 'processing' + + /** + * Donation + */ + // complete dialog for donation const isComplete = step === 'complete' + // set donation amount + const isSetAmount = step === 'setAmount' + // confirm donation amount const isConfirm = step === 'confirm' + + /** + * Password + */ + // wrong password const isPasswordInvalid = step === 'passwordInvalid' - const isProcessing = step === 'processing' const isResetPasswordComplete = step === 'resetPasswordComplete' const isResetPasswordConfirm = step === 'resetPasswordConfirm' const isResetPasswordRequest = step === 'resetPasswordRequest' - const isSetAmount = step === 'setAmount' const isSetPaymentPassword = step === 'setPaymentPassword' + const isHKD = currency === CURRENCY.HKD + useEffect(() => { + analytics.trackEvent('view_add_credit_dialog', { step }) + }, [step]) + return ( <> {children({ open })} From 33ed2a70cfbc3672fd72cc0fba41f30929d24d13 Mon Sep 17 00:00:00 2001 From: RoberTu Date: Mon, 13 Jul 2020 15:55:32 +0800 Subject: [PATCH 28/86] feat(public-private-queries): `` --- src/components/GQL/queries/userArticles.ts | 3 +- .../UserProfile/DropdownActions/index.tsx | 29 +++--- .../ProfileCoverUploader/index.tsx | 4 +- .../EditProfileButton/ProfileEditor/index.tsx | 4 +- src/components/UserProfile/gql.ts | 69 +++++++++++++ src/components/UserProfile/index.tsx | 97 ++++++------------- 6 files changed, 123 insertions(+), 83 deletions(-) create mode 100644 src/components/UserProfile/gql.ts diff --git a/src/components/GQL/queries/userArticles.ts b/src/components/GQL/queries/userArticles.ts index e9b6d15fb6..a7e1a68d88 100644 --- a/src/components/GQL/queries/userArticles.ts +++ b/src/components/GQL/queries/userArticles.ts @@ -11,8 +11,7 @@ export default gql` description profileCover } - articles(input: { first: 10, after: $after }) - @connection(key: "userArticles") { + articles(input: { first: 10, after: $after }) { totalCount pageInfo { startCursor diff --git a/src/components/UserProfile/DropdownActions/index.tsx b/src/components/UserProfile/DropdownActions/index.tsx index 7347ea60cd..5fabd64e72 100644 --- a/src/components/UserProfile/DropdownActions/index.tsx +++ b/src/components/UserProfile/DropdownActions/index.tsx @@ -15,22 +15,29 @@ import { BlockUser } from '~/components/BlockUser' import { TEXT } from '~/common/enums' -import { DropdownActionsUser } from './__generated__/DropdownActionsUser' +import { DropdownActionsUserPublic } from './__generated__/DropdownActionsUserPublic' const fragments = { - user: gql` - fragment DropdownActionsUser on User { - id - ...BlockUserPublic - ...BlockUserPrivate - } - ${BlockUser.fragments.user.public} - ${BlockUser.fragments.user.private} - `, + user: { + public: gql` + fragment DropdownActionsUserPublic on User { + id + ...BlockUserPublic + } + ${BlockUser.fragments.user.public} + `, + private: gql` + fragment DropdownActionsUserPrivate on User { + id + ...BlockUserPrivate + } + ${BlockUser.fragments.user.private} + `, + }, } interface DropdownActionsProps { - user: DropdownActionsUser + user: DropdownActionsUserPublic isMe: boolean } diff --git a/src/components/UserProfile/EditProfileButton/ProfileEditor/ProfileCoverUploader/index.tsx b/src/components/UserProfile/EditProfileButton/ProfileEditor/ProfileCoverUploader/index.tsx index 4be2b51efa..8d7a74e45c 100644 --- a/src/components/UserProfile/EditProfileButton/ProfileEditor/ProfileCoverUploader/index.tsx +++ b/src/components/UserProfile/EditProfileButton/ProfileEditor/ProfileCoverUploader/index.tsx @@ -21,7 +21,7 @@ import Cover from '../../../Cover' import styles from './styles.css' import { SingleFileUpload } from '~/components/GQL/mutations/__generated__/SingleFileUpload' -import { ProfileUser } from '~/components/UserProfile/__generated__/ProfileUser' +import { ProfileUserPublic } from '~/components/UserProfile/__generated__/ProfileUserPublic' /** * This component is for uploading profile cover. @@ -34,7 +34,7 @@ import { ProfileUser } from '~/components/UserProfile/__generated__/ProfileUser' */ interface Props { - user: ProfileUser + user: ProfileUserPublic onUpload: (assetId: string | null) => void } diff --git a/src/components/UserProfile/EditProfileButton/ProfileEditor/index.tsx b/src/components/UserProfile/EditProfileButton/ProfileEditor/index.tsx index bb53ead644..2235c9a051 100644 --- a/src/components/UserProfile/EditProfileButton/ProfileEditor/index.tsx +++ b/src/components/UserProfile/EditProfileButton/ProfileEditor/index.tsx @@ -23,10 +23,10 @@ import { import ProfileCoverUploader from './ProfileCoverUploader' import styles from './styles.css' -import { ProfileUser } from '~/components/UserProfile/__generated__/ProfileUser' +import { ProfileUserPublic } from '~/components/UserProfile/__generated__/ProfileUserPublic' import { UpdateUserInfoProfile } from './__generated__/UpdateUserInfoProfile' -export type ProfileEditorUser = ProfileUser +export type ProfileEditorUser = ProfileUserPublic interface FormProps { user: ProfileEditorUser diff --git a/src/components/UserProfile/gql.ts b/src/components/UserProfile/gql.ts new file mode 100644 index 0000000000..3f83dbc440 --- /dev/null +++ b/src/components/UserProfile/gql.ts @@ -0,0 +1,69 @@ +import gql from 'graphql-tag' + +import { Avatar, FollowButton } from '~/components' + +import DropdownActions from './DropdownActions' + +const fragments = { + user: { + public: gql` + fragment ProfileUserPublic on User { + id + userName + displayName + liker { + civicLiker + } + info { + badges { + type + } + description + profileCover + } + followees(input: { first: 0 }) { + totalCount + } + followers(input: { first: 0 }) { + totalCount + } + status { + state + } + ...AvatarUser + ...DropdownActionsUserPublic + } + ${Avatar.fragments.user} + ${DropdownActions.fragments.user.public} + `, + private: gql` + fragment ProfileUserPrivate on User { + id + ...FollowButtonUserPrivate + ...DropdownActionsUserPrivate + } + ${FollowButton.fragments.user.private} + ${DropdownActions.fragments.user.private} + `, + }, +} + +export const USER_PROFILE_PUBLIC = gql` + query UserProfileUserPublic($userName: String!) { + user(input: { userName: $userName }) { + ...ProfileUserPublic + ...ProfileUserPrivate + } + } + ${fragments.user.public} + ${fragments.user.private} +` + +export const USER_PROFILE_PRIVATE = gql` + query UserProfileUserPrivate($userName: String!) { + user(input: { userName: $userName }) { + ...ProfileUserPrivate + } + } + ${fragments.user.private} +` diff --git a/src/components/UserProfile/index.tsx b/src/components/UserProfile/index.tsx index 085f77bf35..23cc68c7c9 100644 --- a/src/components/UserProfile/index.tsx +++ b/src/components/UserProfile/index.tsx @@ -1,10 +1,8 @@ import { useQuery } from '@apollo/react-hooks' -import gql from 'graphql-tag' -import _get from 'lodash/get' import _some from 'lodash/some' import Link from 'next/link' import { useRouter } from 'next/router' -import { useContext } from 'react' +import { useContext, useEffect } from 'react' import { Avatar, @@ -25,79 +23,45 @@ import { CivicLikerBadge, SeedBadge } from './Badges' import Cover from './Cover' import DropdownActions from './DropdownActions' import EditProfileButton from './EditProfileButton' +import { USER_PROFILE_PRIVATE, USER_PROFILE_PUBLIC } from './gql' import styles from './styles.css' -import { MeProfileUser } from './__generated__/MeProfileUser' -import { UserProfileUser } from './__generated__/UserProfileUser' - -const fragments = { - user: gql` - fragment ProfileUser on User { - id - userName - displayName - liker { - civicLiker - } - info { - badges { - type - } - description - profileCover - } - followees(input: { first: 0 }) { - totalCount - } - followers(input: { first: 0 }) { - totalCount - } - status { - state - } - ...AvatarUser - ...FollowButtonUserPrivate @skip(if: $isMe) - ...DropdownActionsUser - } - ${Avatar.fragments.user} - ${FollowButton.fragments.user.private} - ${DropdownActions.fragments.user} - `, -} - -const USER_PROFILE = gql` - query UserProfileUser($userName: String!, $isMe: Boolean = false) { - user(input: { userName: $userName }) { - ...ProfileUser - } - } - ${fragments.user} -` - -const ME_PROFILE = gql` - query MeProfileUser($isMe: Boolean = true) { - viewer { - ...ProfileUser - } - } - ${fragments.user} -` +import { UserProfileUserPublic } from './__generated__/UserProfileUserPublic' export const UserProfile = () => { const isSmallUp = useResponsive('sm-up') const router = useRouter() const viewer = useContext(ViewerContext) + // public data const userName = getQuery({ router, key: 'userName' }) const isMe = !userName || viewer.userName === userName - const { data, loading } = useQuery( - isMe ? ME_PROFILE : USER_PROFILE, + const { data, loading, client } = useQuery( + USER_PROFILE_PUBLIC, { - variables: isMe ? {} : { userName }, + variables: { userName }, } ) - const user = isMe ? _get(data, 'viewer') : _get(data, 'user') + const user = data?.user + // fetch private data + useEffect(() => { + if (!viewer.id || !user) { + return + } + + client.query({ + query: USER_PROFILE_PRIVATE, + fetchPolicy: 'network-only', + variables: { + userName, + }, + }) + }, [userName, viewer.id, user]) + + /** + * Render + */ const LayoutHeader = () => ( { const userFollowersPath = toPath({ page: 'userFollowers', - userName: user.userName, + userName, }) const userFolloweesPath = toPath({ page: 'userFollowees', - userName: user.userName, + userName, }) const badges = user.info.badges || [] const hasSeedBadge = _some(badges, { type: 'seed' }) const profileCover = user.info.profileCover || '' + const userState = user.status?.state as string const isCivicLiker = user.liker.civicLiker - const isUserArchived = user.status.state === 'archived' - const isUserBanned = user.status.state === 'banned' + const isUserArchived = userState === 'archived' + const isUserBanned = userState === 'banned' const isUserInactive = isUserArchived || isUserBanned /** From 1b43619fd7c06caf1041299a2c18f293f69af424 Mon Sep 17 00:00:00 2001 From: RoberTu Date: Mon, 13 Jul 2020 16:36:30 +0800 Subject: [PATCH 29/86] feat(public-private-queries): `` --- src/components/ArticleDigest/Feed/index.tsx | 61 +++++++----- .../ArticleDigest/FooterActions/index.tsx | 42 ++++---- src/components/GQL/queries/tagArticles.ts | 6 +- src/components/GQL/queries/userArticles.ts | 22 ++++- src/components/GQL/updates/userArticles.ts | 8 +- src/components/Interaction/InfiniteScroll.tsx | 3 +- src/components/UserProfile/index.tsx | 2 +- src/pages/recommendation.tsx | 6 +- src/views/ArticleFeed/index.tsx | 12 ++- .../Follow/FollowFeed/ArticlesFeed/index.tsx | 6 +- .../Follow/FollowFeed/TagsFeed/index.tsx | 6 +- src/views/Home/Feed/index.tsx | 6 +- src/views/Me/Bookmarks/index.tsx | 6 +- src/views/Me/History/index.tsx | 6 +- src/views/Search/SearchArticles/index.tsx | 6 +- src/views/User/Articles/UserArticles.tsx | 97 +++++++++++++------ 16 files changed, 192 insertions(+), 103 deletions(-) diff --git a/src/components/ArticleDigest/Feed/index.tsx b/src/components/ArticleDigest/Feed/index.tsx index cc58c8d1be..429a6fd033 100644 --- a/src/components/ArticleDigest/Feed/index.tsx +++ b/src/components/ArticleDigest/Feed/index.tsx @@ -30,33 +30,44 @@ type ArticleDigestFeedProps = { } & ArticleDigestFeedControls const fragments = { - article: gql` - fragment ArticleDigestFeedArticle on Article { - id - title - slug - mediaHash - articleState: state - cover - summary - author { + article: { + public: gql` + fragment ArticleDigestFeedArticlePublic on Article { id - userName - ...UserDigestMiniUser + title + slug + mediaHash + articleState: state + cover + summary + author { + id + userName + ...UserDigestMiniUser + } + ...CreatedAtArticle + ...InactiveStateArticle + ...ArticleDigestTitleArticle + ...DropdownActionsArticle + ...FooterActionsArticlePublic + ...FooterActionsArticlePrivate } - ...CreatedAtArticle - ...InactiveStateArticle - ...ArticleDigestTitleArticle - ...FooterActionsArticle - ...DropdownActionsArticle - } - ${UserDigest.Mini.fragments.user} - ${CreatedAt.fragments.article} - ${InactiveState.fragments.article} - ${ArticleDigestTitle.fragments.article} - ${FooterActions.fragments.article} - ${DropdownActions.fragments.article} - `, + ${UserDigest.Mini.fragments.user} + ${CreatedAt.fragments.article} + ${InactiveState.fragments.article} + ${ArticleDigestTitle.fragments.article} + ${DropdownActions.fragments.article} + ${FooterActions.fragments.article.public} + ${FooterActions.fragments.article.private} + `, + private: gql` + fragment ArticleDigestFeedArticlePrivate on Article { + id + ...FooterActionsArticlePrivate + } + ${FooterActions.fragments.article.private} + `, + }, } const BaseArticleDigestFeed = ({ diff --git a/src/components/ArticleDigest/FooterActions/index.tsx b/src/components/ArticleDigest/FooterActions/index.tsx index f2f9e077a5..4c0073d7bd 100644 --- a/src/components/ArticleDigest/FooterActions/index.tsx +++ b/src/components/ArticleDigest/FooterActions/index.tsx @@ -19,26 +19,32 @@ type FooterActionsProps = { } & FooterActionsControls const fragments = { - article: gql` - fragment FooterActionsArticle on Article { - id - title - slug - mediaHash - author { + article: { + public: gql` + fragment FooterActionsArticlePublic on Article { id - userName + title + slug + mediaHash + author { + id + userName + } + ...AppreciationArticle + ...ActionsResponseCountArticle + ...DropdownActionsArticle } - ...AppreciationArticle - ...ActionsResponseCountArticle - ...BookmarkArticlePrivate - ...DropdownActionsArticle - } - ${Appreciation.fragments.article} - ${ResponseCount.fragments.article} - ${BookmarkButton.fragments.article.private} - ${DropdownActions.fragments.article} - `, + ${Appreciation.fragments.article} + ${ResponseCount.fragments.article} + ${DropdownActions.fragments.article} + `, + private: gql` + fragment FooterActionsArticlePrivate on Article { + ...BookmarkArticlePrivate + } + ${BookmarkButton.fragments.article.private} + `, + }, } const FooterActions = ({ diff --git a/src/components/GQL/queries/tagArticles.ts b/src/components/GQL/queries/tagArticles.ts index eac0f3dcbf..a4a124c870 100644 --- a/src/components/GQL/queries/tagArticles.ts +++ b/src/components/GQL/queries/tagArticles.ts @@ -16,12 +16,14 @@ export default gql` edges { cursor node { - ...ArticleDigestFeedArticle + ...ArticleDigestFeedArticlePublic + ...ArticleDigestFeedArticlePrivate } } } } } } - ${ArticleDigestFeed.fragments.article} + ${ArticleDigestFeed.fragments.article.public} + ${ArticleDigestFeed.fragments.article.private} ` diff --git a/src/components/GQL/queries/userArticles.ts b/src/components/GQL/queries/userArticles.ts index a7e1a68d88..0f5974dcd7 100644 --- a/src/components/GQL/queries/userArticles.ts +++ b/src/components/GQL/queries/userArticles.ts @@ -2,8 +2,8 @@ import gql from 'graphql-tag' import { ArticleDigestFeed } from '~/components' -export default gql` - query UserArticles($userName: String!, $after: String) { +export const USER_ARTICLES_PUBLIC = gql` + query UserArticlesPublic($userName: String!, $after: String) { user(input: { userName: $userName }) { id displayName @@ -23,7 +23,8 @@ export default gql` node { createdAt wordCount - ...ArticleDigestFeedArticle + ...ArticleDigestFeedArticlePublic + ...ArticleDigestFeedArticlePrivate } } } @@ -34,5 +35,18 @@ export default gql` } } } - ${ArticleDigestFeed.fragments.article} + ${ArticleDigestFeed.fragments.article.public} + ${ArticleDigestFeed.fragments.article.private} +` + +export const USER_ARTICLES_PRIVATE = gql` + query UserArticlesPrivate($ids: [ID!]!) { + nodes(input: { ids: $ids }) { + id + ... on Article { + ...ArticleDigestFeedArticlePrivate + } + } + } + ${ArticleDigestFeed.fragments.article.private} ` diff --git a/src/components/GQL/updates/userArticles.ts b/src/components/GQL/updates/userArticles.ts index 0e7a537a30..d91e2d809a 100644 --- a/src/components/GQL/updates/userArticles.ts +++ b/src/components/GQL/updates/userArticles.ts @@ -26,7 +26,9 @@ const update = ({ type: 'sticky' | 'unsticky' | 'archive' }) => { // FIXME: circular dependencies - const USER_ARTICLES = require('~/components/GQL/queries/userArticles').default + const { + USER_ARTICLES_PUBLIC, + } = require('~/components/GQL/queries/userArticles').default if (!userName) { return @@ -34,7 +36,7 @@ const update = ({ try { const data = cache.readQuery({ - query: USER_ARTICLES, + query: USER_ARTICLES_PUBLIC, variables: { userName }, }) @@ -72,7 +74,7 @@ const update = ({ } cache.writeQuery({ - query: USER_ARTICLES, + query: USER_ARTICLES_PUBLIC, variables: { userName }, data: { user: { diff --git a/src/components/Interaction/InfiniteScroll.tsx b/src/components/Interaction/InfiniteScroll.tsx index 9a639c60ac..c53c423642 100644 --- a/src/components/Interaction/InfiniteScroll.tsx +++ b/src/components/Interaction/InfiniteScroll.tsx @@ -1,4 +1,3 @@ -import { ApolloQueryResult } from 'apollo-client' import { forwardRef, Ref } from 'react' import { Waypoint } from 'react-waypoint' @@ -34,7 +33,7 @@ interface Props { /** * Callback to load more entities */ - loadMore: () => Promise> + loadMore: () => Promise /** * A React component to act as loader diff --git a/src/components/UserProfile/index.tsx b/src/components/UserProfile/index.tsx index 23cc68c7c9..158b2cc2d8 100644 --- a/src/components/UserProfile/index.tsx +++ b/src/components/UserProfile/index.tsx @@ -57,7 +57,7 @@ export const UserProfile = () => { userName, }, }) - }, [userName, viewer.id, user]) + }, [user?.id, viewer.id]) /** * Render diff --git a/src/pages/recommendation.tsx b/src/pages/recommendation.tsx index 08a99cc8b8..150a99856a 100644 --- a/src/pages/recommendation.tsx +++ b/src/pages/recommendation.tsx @@ -30,14 +30,16 @@ const query = gql` cursor node { id - ...ArticleDigestFeedArticle + ...ArticleDigestFeedArticlePublic + ...ArticleDigestFeedArticlePrivate } } } } } } - ${ArticleDigestFeed.fragments.article} + ${ArticleDigestFeed.fragments.article.public} + ${ArticleDigestFeed.fragments.article.private} ` const Feed = () => { diff --git a/src/views/ArticleFeed/index.tsx b/src/views/ArticleFeed/index.tsx index 3d20f92150..d4fa496465 100644 --- a/src/views/ArticleFeed/index.tsx +++ b/src/views/ArticleFeed/index.tsx @@ -36,14 +36,16 @@ const QUERIES = { edges { cursor node { - ...ArticleDigestFeedArticle + ...ArticleDigestFeedArticlePublic + ...ArticleDigestFeedArticlePrivate } } } } } } - ${ArticleDigestFeed.fragments.article} + ${ArticleDigestFeed.fragments.article.public} + ${ArticleDigestFeed.fragments.article.private} `, icymi: gql` query AllIcymis($after: String) { @@ -59,14 +61,16 @@ const QUERIES = { edges { cursor node { - ...ArticleDigestFeedArticle + ...ArticleDigestFeedArticlePublic + ...ArticleDigestFeedArticlePrivate } } } } } } - ${ArticleDigestFeed.fragments.article} + ${ArticleDigestFeed.fragments.article.public} + ${ArticleDigestFeed.fragments.article.private} `, } diff --git a/src/views/Follow/FollowFeed/ArticlesFeed/index.tsx b/src/views/Follow/FollowFeed/ArticlesFeed/index.tsx index 9ee5bc2859..34145f5c99 100644 --- a/src/views/Follow/FollowFeed/ArticlesFeed/index.tsx +++ b/src/views/Follow/FollowFeed/ArticlesFeed/index.tsx @@ -30,7 +30,8 @@ const FOLLOW_ARTICLES = gql` node { __typename ... on Article { - ...ArticleDigestFeedArticle + ...ArticleDigestFeedArticlePublic + ...ArticleDigestFeedArticlePrivate } } } @@ -38,7 +39,8 @@ const FOLLOW_ARTICLES = gql` } } } - ${ArticleDigestFeed.fragments.article} + ${ArticleDigestFeed.fragments.article.public} + ${ArticleDigestFeed.fragments.article.private} ` const ArticlesFeed = () => { diff --git a/src/views/Follow/FollowFeed/TagsFeed/index.tsx b/src/views/Follow/FollowFeed/TagsFeed/index.tsx index 99e169029b..037f2acd05 100644 --- a/src/views/Follow/FollowFeed/TagsFeed/index.tsx +++ b/src/views/Follow/FollowFeed/TagsFeed/index.tsx @@ -64,7 +64,8 @@ const FOLLOWING_TAGS_ARTICLES = gql` ...DigestTag createdAt } - ...ArticleDigestFeedArticle + ...ArticleDigestFeedArticlePublic + ...ArticleDigestFeedArticlePrivate } } } @@ -72,7 +73,8 @@ const FOLLOWING_TAGS_ARTICLES = gql` } } } - ${ArticleDigestFeed.fragments.article} + ${ArticleDigestFeed.fragments.article.public} + ${ArticleDigestFeed.fragments.article.private} ${Tag.fragments.tag} ` diff --git a/src/views/Home/Feed/index.tsx b/src/views/Home/Feed/index.tsx index 000f56d07f..4ee67bf9c5 100644 --- a/src/views/Home/Feed/index.tsx +++ b/src/views/Home/Feed/index.tsx @@ -58,11 +58,13 @@ const feedFragment = gql` edges { cursor node { - ...ArticleDigestFeedArticle + ...ArticleDigestFeedArticlePublic + ...ArticleDigestFeedArticlePrivate } } } - ${ArticleDigestFeed.fragments.article} + ${ArticleDigestFeed.fragments.article.public} + ${ArticleDigestFeed.fragments.article.private} ` export const queries = { diff --git a/src/views/Me/Bookmarks/index.tsx b/src/views/Me/Bookmarks/index.tsx index 7994f074a1..bd1386e066 100644 --- a/src/views/Me/Bookmarks/index.tsx +++ b/src/views/Me/Bookmarks/index.tsx @@ -29,13 +29,15 @@ const ME_BOOKMARK_FEED = gql` edges { cursor node { - ...ArticleDigestFeedArticle + ...ArticleDigestFeedArticlePublic + ...ArticleDigestFeedArticlePrivate } } } } } - ${ArticleDigestFeed.fragments.article} + ${ArticleDigestFeed.fragments.article.public} + ${ArticleDigestFeed.fragments.article.private} ` const MeBookmarks = () => { diff --git a/src/views/Me/History/index.tsx b/src/views/Me/History/index.tsx index 7887e1ea36..9ff281d5cb 100644 --- a/src/views/Me/History/index.tsx +++ b/src/views/Me/History/index.tsx @@ -31,7 +31,8 @@ const ME_HISTORY_FEED = gql` cursor node { article { - ...ArticleDigestFeedArticle + ...ArticleDigestFeedArticlePublic + ...ArticleDigestFeedArticlePrivate } } } @@ -39,7 +40,8 @@ const ME_HISTORY_FEED = gql` } } } - ${ArticleDigestFeed.fragments.article} + ${ArticleDigestFeed.fragments.article.public} + ${ArticleDigestFeed.fragments.article.private} ` const MeHistory = () => { diff --git a/src/views/Search/SearchArticles/index.tsx b/src/views/Search/SearchArticles/index.tsx index 8ff731334d..7192b2f329 100644 --- a/src/views/Search/SearchArticles/index.tsx +++ b/src/views/Search/SearchArticles/index.tsx @@ -30,13 +30,15 @@ const SEARCH_ARTICLES = gql` cursor node { ... on Article { - ...ArticleDigestFeedArticle + ...ArticleDigestFeedArticlePublic + ...ArticleDigestFeedArticlePrivate } } } } } - ${ArticleDigestFeed.fragments.article} + ${ArticleDigestFeed.fragments.article.public} + ${ArticleDigestFeed.fragments.article.private} ` const SearchArticles = () => { diff --git a/src/views/User/Articles/UserArticles.tsx b/src/views/User/Articles/UserArticles.tsx index 67dcc085ab..380aa09450 100644 --- a/src/views/User/Articles/UserArticles.tsx +++ b/src/views/User/Articles/UserArticles.tsx @@ -1,6 +1,6 @@ import { useQuery } from '@apollo/react-hooks' import { useRouter } from 'next/router' -import { useContext } from 'react' +import { useContext, useEffect } from 'react' import { ArticleDigestFeed, @@ -15,7 +15,10 @@ import { ViewerContext, } from '~/components' import { QueryError } from '~/components/GQL' -import USER_ARTICLES from '~/components/GQL/queries/userArticles' +import { + USER_ARTICLES_PRIVATE, + USER_ARTICLES_PUBLIC, +} from '~/components/GQL/queries/userArticles' import { analytics, getQuery, mergeConnections } from '~/common/utils' @@ -25,11 +28,11 @@ import UserTabs from '../UserTabs' import styles from './styles.css' import { - UserArticles as UserArticlesTypes, - UserArticles_user, -} from '~/components/GQL/queries/__generated__/UserArticles' + UserArticlesPublic, + UserArticlesPublic_user, +} from '~/components/GQL/queries/__generated__/UserArticlesPublic' -const ArticleSummaryInfo = ({ user }: { user: UserArticles_user }) => { +const ArticleSummaryInfo = ({ user }: { user: UserArticlesPublic_user }) => { const { articleCount: articles, totalWordCount: words } = user.status || { articleCount: 0, totalWordCount: 0, @@ -57,14 +60,69 @@ const UserArticles = () => { const router = useRouter() const userName = getQuery({ router, key: 'userName' }) - const { data, loading, error, fetchMore, refetch } = useQuery< - UserArticlesTypes - >(USER_ARTICLES, { variables: { userName } }) + /** + * Data Fetching + */ + // public data + const { data, loading, error, fetchMore, refetch, client } = useQuery< + UserArticlesPublic + >(USER_ARTICLES_PUBLIC, { variables: { userName } }) + + // pagination + const connectionPath = 'user.articles' const user = data?.user + const { edges, pageInfo } = user?.articles || {} + // private data + const loadPrivate = (publicData?: UserArticlesPublic) => { + if (!viewer.id || !publicData || !user) { + return + } + + const publiceEdges = publicData.user?.articles?.edges || [] + const publicIds = publiceEdges.map(({ node }) => node.id) + + client.query({ + query: USER_ARTICLES_PRIVATE, + fetchPolicy: 'network-only', + variables: { ids: publicIds }, + }) + } + + // fetch private data for first page + useEffect(() => { + loadPrivate(data) + }, [user?.id, viewer.id]) + + // load next page + const loadMore = async () => { + analytics.trackEvent('load_more', { + type: 'user_article', + location: edges?.length || 0, + }) + + const { data: newData } = await fetchMore({ + variables: { + after: pageInfo?.endCursor, + }, + updateQuery: (previousResult, { fetchMoreResult }) => + mergeConnections({ + oldData: previousResult, + newData: fetchMoreResult, + path: connectionPath, + }), + }) + + loadPrivate(newData) + } + + // pull to refresh usePullToRefresh.Register() usePullToRefresh.Handler(refetch) + /** + * Render + */ if (loading) { return } @@ -77,9 +135,6 @@ const UserArticles = () => { return null } - const connectionPath = 'user.articles' - const { edges, pageInfo } = user.articles - const CustomHead = () => ( { ) } - const loadMore = () => { - analytics.trackEvent('load_more', { - type: 'user_article', - location: edges.length, - }) - return fetchMore({ - variables: { - after: pageInfo.endCursor, - }, - updateQuery: (previousResult, { fetchMoreResult }) => - mergeConnections({ - oldData: previousResult, - newData: fetchMoreResult, - path: connectionPath, - }), - }) - } - return ( <> From b8dd74841f54eebba2bd9b5f93bdbca7f7a6b994 Mon Sep 17 00:00:00 2001 From: RoberTu Date: Mon, 13 Jul 2020 17:03:30 +0800 Subject: [PATCH 30/86] fix(types): fix types error --- src/components/ArticleDigest/Feed/index.tsx | 4 ++-- src/components/ArticleDigest/FooterActions/index.tsx | 4 ++-- src/components/GQL/updates/userArticles.ts | 12 ++++++------ 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/components/ArticleDigest/Feed/index.tsx b/src/components/ArticleDigest/Feed/index.tsx index 429a6fd033..a2a888e63e 100644 --- a/src/components/ArticleDigest/Feed/index.tsx +++ b/src/components/ArticleDigest/Feed/index.tsx @@ -17,7 +17,7 @@ import InactiveState from './InactiveState' import styles from './styles.css' import { ClientPreference } from '~/components/GQL/queries/__generated__/ClientPreference' -import { ArticleDigestFeedArticle } from './__generated__/ArticleDigestFeedArticle' +import { ArticleDigestFeedArticlePublic } from './__generated__/ArticleDigestFeedArticlePublic' export type ArticleDigestFeedControls = { onClick?: () => any @@ -26,7 +26,7 @@ export type ArticleDigestFeedControls = { } & FooterActionsControls type ArticleDigestFeedProps = { - article: ArticleDigestFeedArticle + article: ArticleDigestFeedArticlePublic } & ArticleDigestFeedControls const fragments = { diff --git a/src/components/ArticleDigest/FooterActions/index.tsx b/src/components/ArticleDigest/FooterActions/index.tsx index 4c0073d7bd..a9a00d0e00 100644 --- a/src/components/ArticleDigest/FooterActions/index.tsx +++ b/src/components/ArticleDigest/FooterActions/index.tsx @@ -10,12 +10,12 @@ import Appreciation from './Appreciation' import ResponseCount from './ResponseCount' import styles from './styles.css' -import { FooterActionsArticle } from './__generated__/FooterActionsArticle' +import { FooterActionsArticlePublic } from './__generated__/FooterActionsArticlePublic' export type FooterActionsControls = DropdownActionsControls type FooterActionsProps = { - article: FooterActionsArticle + article: FooterActionsArticlePublic } & FooterActionsControls const fragments = { diff --git a/src/components/GQL/updates/userArticles.ts b/src/components/GQL/updates/userArticles.ts index d91e2d809a..714006805d 100644 --- a/src/components/GQL/updates/userArticles.ts +++ b/src/components/GQL/updates/userArticles.ts @@ -1,12 +1,12 @@ import { DataProxy } from 'apollo-cache' import { - UserArticles, - UserArticles_user_articles_edges, -} from '~/components/GQL/queries/__generated__/UserArticles' + UserArticlesPublic, + UserArticlesPublic_user_articles_edges, +} from '~/components/GQL/queries/__generated__/UserArticlesPublic' const sortEdgesByCreatedAtDesc = ( - edges: UserArticles_user_articles_edges[] + edges: UserArticlesPublic_user_articles_edges[] ) => { return edges.sort( ({ node: n1 }, { node: n2 }) => @@ -28,14 +28,14 @@ const update = ({ // FIXME: circular dependencies const { USER_ARTICLES_PUBLIC, - } = require('~/components/GQL/queries/userArticles').default + } = require('~/components/GQL/queries/userArticlesPublic').default if (!userName) { return } try { - const data = cache.readQuery({ + const data = cache.readQuery({ query: USER_ARTICLES_PUBLIC, variables: { userName }, }) From 4e63abe5395254494ff25a7cd3788d2abb593390 Mon Sep 17 00:00:00 2001 From: RoberTu Date: Mon, 13 Jul 2020 17:06:45 +0800 Subject: [PATCH 31/86] fix(types): fix types error --- src/components/GQL/updates/userArticles.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/GQL/updates/userArticles.ts b/src/components/GQL/updates/userArticles.ts index 714006805d..9870c8a623 100644 --- a/src/components/GQL/updates/userArticles.ts +++ b/src/components/GQL/updates/userArticles.ts @@ -28,7 +28,7 @@ const update = ({ // FIXME: circular dependencies const { USER_ARTICLES_PUBLIC, - } = require('~/components/GQL/queries/userArticlesPublic').default + } = require('~/components/GQL/queries/userArticles').default if (!userName) { return From ffcf7bb1500badc15cd5271b27025d86641dca47 Mon Sep 17 00:00:00 2001 From: RoberTu Date: Mon, 13 Jul 2020 18:18:04 +0800 Subject: [PATCH 32/86] feat(public-private-queries): `` & `` --- src/views/User/Followees/UserFollowees.tsx | 119 +++++++++++--------- src/views/User/Followees/gql.ts | 40 +++++++ src/views/User/Followers/UserFollowers.tsx | 120 ++++++++++++--------- src/views/User/Followers/gql.ts | 40 +++++++ 4 files changed, 213 insertions(+), 106 deletions(-) create mode 100644 src/views/User/Followees/gql.ts create mode 100644 src/views/User/Followers/gql.ts diff --git a/src/views/User/Followees/UserFollowees.tsx b/src/views/User/Followees/UserFollowees.tsx index 0306a1cb00..18313fdf09 100644 --- a/src/views/User/Followees/UserFollowees.tsx +++ b/src/views/User/Followees/UserFollowees.tsx @@ -1,6 +1,6 @@ import { useQuery } from '@apollo/react-hooks' -import gql from 'graphql-tag' import { useRouter } from 'next/router' +import { useContext, useEffect } from 'react' import { EmptyWarning, @@ -10,52 +10,87 @@ import { Spinner, Translate, usePullToRefresh, + ViewerContext, } from '~/components' import { QueryError } from '~/components/GQL' import { UserDigest } from '~/components/UserDigest' import { analytics, getQuery, mergeConnections } from '~/common/utils' -import { UserFolloweeFeed } from './__generated__/UserFolloweeFeed' - -const USER_FOLLOWEES_FEED = gql` - query UserFolloweeFeed($userName: String!, $after: String) { - user(input: { userName: $userName }) { - id - displayName - followees(input: { first: 20, after: $after }) { - pageInfo { - startCursor - endCursor - hasNextPage - } - edges { - cursor - node { - ...UserDigestRichUserPublic - ...UserDigestRichUserPrivate - } - } - } - } - } - ${UserDigest.Rich.fragments.user.public} - ${UserDigest.Rich.fragments.user.private} -` +import { USER_FOLLOWEES_PRIVATE, USER_FOLLOWEES_PUBLIC } from './gql' + +import { UserFolloweePublic } from './__generated__/UserFolloweePublic' const UserFollowees = () => { + const viewer = useContext(ViewerContext) const router = useRouter() const userName = getQuery({ router, key: 'userName' }) - const { data, loading, error, fetchMore, refetch } = useQuery< - UserFolloweeFeed - >(USER_FOLLOWEES_FEED, { + + /** + * Data Fetching + */ + // public data + const { data, loading, error, fetchMore, refetch, client } = useQuery< + UserFolloweePublic + >(USER_FOLLOWEES_PUBLIC, { variables: { userName }, }) + // pagination + const user = data?.user + const connectionPath = 'user.followees' + const { edges, pageInfo } = user?.followees || {} + + // private data + const loadPrivate = (publicData?: UserFolloweePublic) => { + if (!viewer.id || !publicData || !user) { + return + } + + const publiceEdges = publicData.user?.followees.edges || [] + const publicIds = publiceEdges.map(({ node }) => node.id) + + client.query({ + query: USER_FOLLOWEES_PRIVATE, + fetchPolicy: 'network-only', + variables: { ids: publicIds }, + }) + } + + // fetch private data for first page + useEffect(() => { + loadPrivate(data) + }, [user?.id, viewer.id]) + + // load next page + const loadMore = async () => { + analytics.trackEvent('load_more', { + type: 'followee', + location: edges?.length || 0, + }) + const { data: newData } = await fetchMore({ + variables: { + after: pageInfo?.endCursor, + }, + updateQuery: (previousResult, { fetchMoreResult }) => + mergeConnections({ + oldData: previousResult, + newData: fetchMoreResult, + path: connectionPath, + }), + }) + + loadPrivate(newData) + } + + // pull to refresh usePullToRefresh.Register() usePullToRefresh.Handler(refetch) - if (loading || !data || !data.user) { + /** + * Render + */ + if (loading || !data || !user) { return } @@ -63,10 +98,6 @@ const UserFollowees = () => { return } - const user = data.user - const connectionPath = 'user.followees' - const { edges, pageInfo } = user.followees - if (!edges || edges.length <= 0 || !pageInfo) { return ( { ) } - const loadMore = () => { - analytics.trackEvent('load_more', { - type: 'followee', - location: edges.length, - }) - return fetchMore({ - variables: { - after: pageInfo.endCursor, - }, - updateQuery: (previousResult, { fetchMoreResult }) => - mergeConnections({ - oldData: previousResult, - newData: fetchMoreResult, - path: connectionPath, - }), - }) - } - return ( <> { + const viewer = useContext(ViewerContext) const router = useRouter() const userName = getQuery({ router, key: 'userName' }) - const { data, loading, error, fetchMore, refetch } = useQuery< - UserFollowerFeed - >(USER_FOLLOWERS_FEED, { + + /** + * Data Fetching + */ + // public data + const { data, loading, error, fetchMore, refetch, client } = useQuery< + UserFollowerPublic + >(USER_FOLLOWERS_PUBLIC, { variables: { userName }, }) + // pagination + const user = data?.user + const connectionPath = 'user.followers' + const { edges, pageInfo } = user?.followers || {} + + // private data + const loadPrivate = (publicData?: UserFollowerPublic) => { + if (!viewer.id || !publicData || !user) { + return + } + + const publiceEdges = publicData.user?.followers.edges || [] + const publicIds = publiceEdges.map(({ node }) => node.id) + + client.query({ + query: USER_FOLLOWERS_PRIVATE, + fetchPolicy: 'network-only', + variables: { ids: publicIds }, + }) + } + + // fetch private data for first page + useEffect(() => { + loadPrivate(data) + }, [user?.id, viewer.id]) + + // load next page + const loadMore = async () => { + analytics.trackEvent('load_more', { + type: 'follower', + location: edges?.length || 0, + }) + const { data: newData } = await fetchMore({ + variables: { + after: pageInfo?.endCursor, + }, + updateQuery: (previousResult, { fetchMoreResult }) => + mergeConnections({ + oldData: previousResult, + newData: fetchMoreResult, + path: connectionPath, + }), + }) + + loadPrivate(newData) + } + + // pull to refresh usePullToRefresh.Register() usePullToRefresh.Handler(refetch) - if (loading || !data || !data.user) { + /** + * Render + */ + + if (loading || !data || !user) { return } @@ -63,10 +99,6 @@ const UserFollowers = () => { return } - const user = data.user - const connectionPath = 'user.followers' - const { edges, pageInfo } = user.followers - if (!edges || edges.length <= 0 || !pageInfo) { return ( { ) } - const loadMore = () => { - analytics.trackEvent('load_more', { - type: 'follower', - location: edges.length, - }) - return fetchMore({ - variables: { - after: pageInfo.endCursor, - }, - updateQuery: (previousResult, { fetchMoreResult }) => - mergeConnections({ - oldData: previousResult, - newData: fetchMoreResult, - path: connectionPath, - }), - }) - } - return ( <> Date: Mon, 13 Jul 2020 19:13:21 +0800 Subject: [PATCH 33/86] feat(public-private-queries): `` --- .../index.tsx => UserComments.tsx} | 164 +++++++++--------- src/views/User/Comments/gql.ts | 66 +++++++ 2 files changed, 144 insertions(+), 86 deletions(-) rename src/views/User/Comments/{UserComments/index.tsx => UserComments.tsx} (65%) create mode 100644 src/views/User/Comments/gql.ts diff --git a/src/views/User/Comments/UserComments/index.tsx b/src/views/User/Comments/UserComments.tsx similarity index 65% rename from src/views/User/Comments/UserComments/index.tsx rename to src/views/User/Comments/UserComments.tsx index b607779efb..b592491bde 100644 --- a/src/views/User/Comments/UserComments/index.tsx +++ b/src/views/User/Comments/UserComments.tsx @@ -1,6 +1,7 @@ import { useQuery } from '@apollo/react-hooks' -import gql from 'graphql-tag' +import _flatten from 'lodash/flatten' import { useRouter } from 'next/router' +import { useContext, useEffect } from 'react' import { ArticleDigestTitle, @@ -12,6 +13,7 @@ import { List, Spinner, usePullToRefresh, + ViewerContext, } from '~/components' import { QueryError } from '~/components/GQL' @@ -24,66 +26,16 @@ import { import IMAGE_LOGO_192 from '@/public/static/icon-192x192.png?url' -import UserTabs from '../../UserTabs' +import UserTabs from '../UserTabs' +import { USER_COMMENTS_PRIVATE, USER_COMMENTS_PUBLIC, USER_ID } from './gql' import { - UserCommentFeed, - UserCommentFeed_node_User_commentedArticles_edges_node_comments_edges_node, -} from './__generated__/UserCommentFeed' + UserCommentsPublic, + UserCommentsPublic_node_User_commentedArticles_edges_node_comments_edges_node, +} from './__generated__/UserCommentsPublic' import { UserIdUser } from './__generated__/UserIdUser' -const USER_ID = gql` - query UserIdUser($userName: String!) { - user(input: { userName: $userName }) { - id - displayName - info { - description - } - status { - state - } - } - } -` - -const USER_COMMENT_FEED = gql` - query UserCommentFeed($id: ID!, $after: String) { - node(input: { id: $id }) { - ... on User { - id - commentedArticles(input: { first: 5, after: $after }) { - pageInfo { - startCursor - endCursor - hasNextPage - } - edges { - cursor - node { - id - ...ArticleDigestTitleArticle - comments(input: { filter: { author: $id }, first: null }) { - edges { - cursor - node { - ...FeedCommentPublic - ...FeedCommentPrivate - } - } - } - } - } - } - } - } - } - ${ArticleDigestTitle.fragments.article} - ${Comment.Feed.fragments.comment.public} - ${Comment.Feed.fragments.comment.private} -` - -const UserCommentsWrap = () => { +const UserComments = () => { const router = useRouter() const userName = getQuery({ router, key: 'userName' }) @@ -115,18 +67,80 @@ const UserCommentsWrap = () => { image={IMAGE_LOGO_192} /> - + ) } -const UserComments = ({ user }: UserIdUser) => { - const { data, loading, error, fetchMore, refetch } = useQuery< - UserCommentFeed - >(USER_COMMENT_FEED, { +const BaseUserComments = ({ user }: UserIdUser) => { + const viewer = useContext(ViewerContext) + + /** + * Data Fetching + */ + // public data + const { data, loading, error, fetchMore, refetch, client } = useQuery< + UserCommentsPublic + >(USER_COMMENTS_PUBLIC, { variables: { id: user?.id }, }) + // pagination + const connectionPath = 'node.commentedArticles' + const { edges, pageInfo } = + (data?.node?.__typename === 'User' && + data.node.commentedArticles && + data.node.commentedArticles) || + {} + + // private data + const loadPrivate = (publicData?: UserCommentsPublic) => { + if (!viewer.id || !publicData || !user) { + return + } + + const articles = + publicData?.node?.__typename === 'User' + ? publicData.node.commentedArticles.edges || [] + : [] + + const publiceNodes = _flatten( + articles.map(({ node }) => + (node.comments.edges || []).map(({ node: comment }) => comment) + ) + ) + const publicIds = publiceNodes.map((comment) => comment.id) + + client.query({ + query: USER_COMMENTS_PRIVATE, + fetchPolicy: 'network-only', + variables: { ids: publicIds }, + }) + } + + // fetch private data for first page + useEffect(() => { + loadPrivate(data) + }, [user?.id, viewer.id]) + + // load next page + const loadMore = async () => { + const { data: newData } = await fetchMore({ + variables: { + after: pageInfo?.endCursor, + }, + updateQuery: (previousResult, { fetchMoreResult }) => + mergeConnections({ + oldData: previousResult, + newData: fetchMoreResult, + path: connectionPath, + }), + }) + + loadPrivate(newData) + } + + // pull to refresh usePullToRefresh.Register() usePullToRefresh.Handler(refetch) @@ -142,32 +156,10 @@ const UserComments = ({ user }: UserIdUser) => { return } - const connectionPath = 'node.commentedArticles' - const { edges, pageInfo } = - (data && - data.node && - data.node.__typename === 'User' && - data.node.commentedArticles && - data.node.commentedArticles) || - {} - if (!edges || edges.length <= 0 || !pageInfo) { return } - const loadMore = () => - fetchMore({ - variables: { - after: pageInfo.endCursor, - }, - updateQuery: (previousResult, { fetchMoreResult }) => - mergeConnections({ - oldData: previousResult, - newData: fetchMoreResult, - path: connectionPath, - }), - }) - return ( @@ -175,7 +167,7 @@ const UserComments = ({ user }: UserIdUser) => { const commentEdges = articleEdge.node.comments.edges const filteredComments = filterComments( (commentEdges || []).map(({ node }) => node) - ) as UserCommentFeed_node_User_commentedArticles_edges_node_comments_edges_node[] + ) as UserCommentsPublic_node_User_commentedArticles_edges_node_comments_edges_node[] if (filteredComments.length <= 0) { return null @@ -219,4 +211,4 @@ const UserComments = ({ user }: UserIdUser) => { ) } -export default UserCommentsWrap +export default UserComments diff --git a/src/views/User/Comments/gql.ts b/src/views/User/Comments/gql.ts new file mode 100644 index 0000000000..c8e199400b --- /dev/null +++ b/src/views/User/Comments/gql.ts @@ -0,0 +1,66 @@ +import gql from 'graphql-tag' + +import { ArticleDigestTitle, Comment } from '~/components' + +export const USER_ID = gql` + query UserIdUser($userName: String!) { + user(input: { userName: $userName }) { + id + displayName + info { + description + } + status { + state + } + } + } +` + +export const USER_COMMENTS_PUBLIC = gql` + query UserCommentsPublic($id: ID!, $after: String) { + node(input: { id: $id }) { + ... on User { + id + commentedArticles(input: { first: 5, after: $after }) { + pageInfo { + startCursor + endCursor + hasNextPage + } + edges { + cursor + node { + id + ...ArticleDigestTitleArticle + comments(input: { filter: { author: $id }, first: null }) { + edges { + cursor + node { + ...FeedCommentPublic + ...FeedCommentPrivate + } + } + } + } + } + } + } + } + } + ${ArticleDigestTitle.fragments.article} + ${Comment.Feed.fragments.comment.public} + ${Comment.Feed.fragments.comment.private} +` + +export const USER_COMMENTS_PRIVATE = gql` + query UserCommentsPrivate($ids: [ID!]!) { + nodes(input: { ids: $ids }) { + id + ... on Comment { + ...FeedCommentPrivate + } + } + } + ${Comment.Feed.fragments.comment.private} +` From 06e7cec04ab9ced7d66539cd4b2a45946c1b935d Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 14 Jul 2020 01:51:41 +0000 Subject: [PATCH 34/86] build(deps-dev): bump @testing-library/react from 10.4.5 to 10.4.6 Bumps [@testing-library/react](https://github.com/testing-library/react-testing-library) from 10.4.5 to 10.4.6. - [Release notes](https://github.com/testing-library/react-testing-library/releases) - [Changelog](https://github.com/testing-library/react-testing-library/blob/master/CHANGELOG.md) - [Commits](https://github.com/testing-library/react-testing-library/compare/v10.4.5...v10.4.6) Signed-off-by: dependabot-preview[bot] --- package-lock.json | 29 +++++++++++------------------ package.json | 2 +- 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/package-lock.json b/package-lock.json index 94a1cfad51..244f7ee2c8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5759,15 +5759,15 @@ "dev": true }, "@testing-library/dom": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-7.20.0.tgz", - "integrity": "sha512-TywaC+qDGm/Ro34kRYkFQPdT+pxSF4UjZGLIqcGfFQH5IGR43Y7sGLPnkieIW/GNsu337oxNsLUAgpI0JWhXHw==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-7.20.2.tgz", + "integrity": "sha512-9KOD0fFCTVFsT1EgB8C5qKs1nV7KdIGe0YIANAKeIDWWC0vwkiLXA/8HlrM2+w7YXiRXIeeHh0LxTYQpvaoGgA==", "dev": true, "requires": { "@babel/runtime": "^7.10.3", "@types/aria-query": "^4.2.0", "aria-query": "^4.2.2", - "dom-accessibility-api": "^0.4.5", + "dom-accessibility-api": "^0.4.6", "pretty-format": "^25.5.0" }, "dependencies": { @@ -5783,14 +5783,13 @@ } }, "@testing-library/react": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-10.4.5.tgz", - "integrity": "sha512-M5A0W4VphBiEm4vgnq7vHC+/e4Bp/3iIOAWap1FtIiA+Zom6BtXpY3RSTOqc8bZsCcu9gFBZ/lxaiMW6uJddWg==", + "version": "10.4.6", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-10.4.6.tgz", + "integrity": "sha512-pVcm8v0HxCEzrtasbC2bdhWM0P5X9o8a/xfBugC97uVVTmhVeGHj+5CdE1JYi/i2K+mCyeq5YqzHLW/gB+K12w==", "dev": true, "requires": { "@babel/runtime": "^7.10.3", - "@testing-library/dom": "^7.17.1", - "semver": "^7.3.2" + "@testing-library/dom": "^7.17.1" }, "dependencies": { "@babel/runtime": { @@ -5801,12 +5800,6 @@ "requires": { "regenerator-runtime": "^0.13.4" } - }, - "semver": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", - "dev": true } } }, @@ -11203,9 +11196,9 @@ } }, "dom-accessibility-api": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.4.5.tgz", - "integrity": "sha512-HcPDilI95nKztbVikaN2vzwvmv0sE8Y2ZJFODy/m15n7mGXLeOKGiys9qWVbFbh+aq/KYj2lqMLybBOkYAEXqg==", + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.4.6.tgz", + "integrity": "sha512-qxFVFR/ymtfamEQT/AsYLe048sitxFCoCHiM+vuOdR3fE94i3so2SCFJxyz/RxV69PZ+9FgToYWOd7eqJqcbYw==", "dev": true }, "dom-helpers": { diff --git a/package.json b/package.json index d919537a9e..92eaf05110 100644 --- a/package.json +++ b/package.json @@ -89,7 +89,7 @@ "devDependencies": { "@babel/plugin-proposal-optional-chaining": "^7.10.4", "@svgr/webpack": "^5.4.0", - "@testing-library/react": "^10.4.5", + "@testing-library/react": "^10.4.6", "@types/autosize": "^3.0.7", "@types/classnames": "^2.2.10", "@types/express": "^4.17.7", From 9f01c0cd4d1cd5f77b6f9b671e66db7c8bb7c3b3 Mon Sep 17 00:00:00 2001 From: RoberTu Date: Tue, 14 Jul 2020 15:00:22 +0800 Subject: [PATCH 35/86] feat(memoized): memoizing `` --- src/components/Comment/Feed/index.tsx | 25 ++++++- src/views/User/Articles/UserArticles.tsx | 45 ++++++------ src/views/User/Comments/UserComments.tsx | 91 ++++++++++++------------ 3 files changed, 89 insertions(+), 72 deletions(-) diff --git a/src/components/Comment/Feed/index.tsx b/src/components/Comment/Feed/index.tsx index 2f6494cbf0..75e17dbc98 100644 --- a/src/components/Comment/Feed/index.tsx +++ b/src/components/Comment/Feed/index.tsx @@ -1,4 +1,5 @@ import { useLazyQuery } from '@apollo/react-hooks' +import React from 'react' import { AvatarSize, UserDigest } from '~/components' @@ -23,7 +24,7 @@ export type CommentProps = { comment: FeedCommentPublic & Partial } & CommentControls -export const Feed = ({ +export const BaseCommentFeed = ({ comment, avatarSize = 'lg', hasUserName, @@ -82,6 +83,24 @@ export const Feed = ({ ) } -Feed.fragments = fragments +/** + * Memoizing + */ +type MemoizedCommentFeed = React.MemoExoticComponent> & { + fragments: typeof fragments +} + +const CommentFeed = React.memo( + BaseCommentFeed, + ({ comment: prevComment }, { comment }) => { + return ( + prevComment.content === comment.content && + prevComment.upvotes === comment.upvotes && + prevComment.downvotes === comment.downvotes + ) + } +) as MemoizedCommentFeed + +CommentFeed.fragments = fragments -export default Feed +export default CommentFeed diff --git a/src/views/User/Articles/UserArticles.tsx b/src/views/User/Articles/UserArticles.tsx index 380aa09450..182644c104 100644 --- a/src/views/User/Articles/UserArticles.tsx +++ b/src/views/User/Articles/UserArticles.tsx @@ -156,6 +156,10 @@ const UserArticles = () => { ) } + const articleEdges = edges.filter( + ({ node }) => node.articleState === 'active' || viewer.id === node.author.id + ) + return ( <> @@ -166,31 +170,22 @@ const UserArticles = () => { - {edges.map(({ node, cursor }, i) => { - if ( - node.articleState !== 'active' && - viewer.id !== node.author.id - ) { - return null - } - - return ( - - - analytics.trackEvent('click_feed', { - type: 'user_article', - contentType: 'article', - styleType: 'no_cover', - location: i, - }) - } - /> - - ) - })} + {articleEdges.map(({ node, cursor }, i) => ( + + + analytics.trackEvent('click_feed', { + type: 'user_article', + contentType: 'article', + styleType: 'no_cover', + location: i, + }) + } + /> + + ))} diff --git a/src/views/User/Comments/UserComments.tsx b/src/views/User/Comments/UserComments.tsx index b592491bde..e95dc73dbd 100644 --- a/src/views/User/Comments/UserComments.tsx +++ b/src/views/User/Comments/UserComments.tsx @@ -35,6 +35,8 @@ import { } from './__generated__/UserCommentsPublic' import { UserIdUser } from './__generated__/UserIdUser' +type CommentedArticleComment = UserCommentsPublic_node_User_commentedArticles_edges_node_comments_edges_node + const UserComments = () => { const router = useRouter() const userName = getQuery({ router, key: 'userName' }) @@ -103,7 +105,6 @@ const BaseUserComments = ({ user }: UserIdUser) => { publicData?.node?.__typename === 'User' ? publicData.node.commentedArticles.edges || [] : [] - const publiceNodes = _flatten( articles.map(({ node }) => (node.comments.edges || []).map(({ node: comment }) => comment) @@ -144,6 +145,9 @@ const BaseUserComments = ({ user }: UserIdUser) => { usePullToRefresh.Register() usePullToRefresh.Handler(refetch) + /** + * Render + */ if (!user || !user.id) { return null } @@ -160,52 +164,51 @@ const BaseUserComments = ({ user }: UserIdUser) => { return } + const articleEdges = edges + .map((edge) => { + const commentEdges = edge.node.comments.edges || [] + const comments = filterComments( + commentEdges.map(({ node }) => node) + ) + return { ...edge, comments } + }) + .filter(({ comments }) => comments.length > 0) + return ( - {edges.map((articleEdge) => { - const commentEdges = articleEdge.node.comments.edges - const filteredComments = filterComments( - (commentEdges || []).map(({ node }) => node) - ) as UserCommentsPublic_node_User_commentedArticles_edges_node_comments_edges_node[] - - if (filteredComments.length <= 0) { - return null - } - - return ( - - - - - - - {filteredComments.map((comment) => ( - - - - - - ))} - - - ) - })} + {articleEdges.map(({ cursor, node, comments }) => ( + + + + + + + {comments.map((comment) => ( + + + + + + ))} + + + ))} ) From 6f126860dc82c9ae94e1523b0f3ada748d756017 Mon Sep 17 00:00:00 2001 From: RoberTu Date: Tue, 14 Jul 2020 15:07:50 +0800 Subject: [PATCH 36/86] fix(memoized): fix React.memo is broken inside `` --- src/components/Interaction/InfiniteScroll.tsx | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/components/Interaction/InfiniteScroll.tsx b/src/components/Interaction/InfiniteScroll.tsx index c53c423642..74ab2abe79 100644 --- a/src/components/Interaction/InfiniteScroll.tsx +++ b/src/components/Interaction/InfiniteScroll.tsx @@ -1,4 +1,3 @@ -import { forwardRef, Ref } from 'react' import { Waypoint } from 'react-waypoint' import { PullToRefresh, Spinner } from '~/components' @@ -53,28 +52,28 @@ export const InfiniteScroll: React.FC = ({ pullToRefresh, children, }) => { - const LoaderWithRef = forwardRef((props, ref: Ref) => ( -
{loader || }
- )) - - const Inner = () => ( -
- {children} - {hasNextPage && ( - - - - )} -
+ const Loader = () => ( + <> + + {loader} + ) if (pullToRefresh) { return ( - + <> + {children} + {hasNextPage && } + ) } - return + return ( + <> + {children} + {hasNextPage && } + + ) } From fc0237214b155d1220bbbd1dc9fceb83c20dc4e5 Mon Sep 17 00:00:00 2001 From: RoberTu Date: Tue, 14 Jul 2020 21:21:21 +0800 Subject: [PATCH 37/86] feat(public-private-queries): `` --- src/common/enums/route.ts | 4 - src/components/Interaction/InfiniteScroll.tsx | 13 +- src/pages/icymi.tsx | 3 - src/pages/topics.tsx | 3 - src/views/ArticleFeed/index.tsx | 155 --------- src/views/Home/Feed/gql.ts | 103 ++++++ src/views/Home/Feed/index.tsx | 295 +++++++++--------- src/views/Home/SectionHeader/index.tsx | 6 +- .../Me/Settings/Settings/UI/ViewMode.tsx | 4 +- 9 files changed, 259 insertions(+), 327 deletions(-) delete mode 100644 src/pages/icymi.tsx delete mode 100644 src/pages/topics.tsx delete mode 100644 src/views/ArticleFeed/index.tsx create mode 100644 src/views/Home/Feed/gql.ts diff --git a/src/common/enums/route.ts b/src/common/enums/route.ts index 08dd2c20ea..bdd405df06 100644 --- a/src/common/enums/route.ts +++ b/src/common/enums/route.ts @@ -12,8 +12,6 @@ type ROUTE_KEY = | 'HOME' | 'FOLLOW' | 'AUTHORS' - | 'TOPICS' - | 'ICYMI' | 'SEARCH' | 'TAGS' | 'TAG_DETAIL' @@ -61,8 +59,6 @@ export const ROUTES: Array<{ { key: 'HOME', pathname: '/' }, { key: 'FOLLOW', pathname: '/follow' }, { key: 'AUTHORS', pathname: '/authors' }, - { key: 'TOPICS', pathname: '/topics' }, - { key: 'ICYMI', pathname: '/icymi' }, { key: 'SEARCH', pathname: '/search' }, // experient page for recommendation engine testing { key: 'RECOMMENDATION', pathname: '/recommendation' }, diff --git a/src/components/Interaction/InfiniteScroll.tsx b/src/components/Interaction/InfiniteScroll.tsx index c53c423642..56074c09b8 100644 --- a/src/components/Interaction/InfiniteScroll.tsx +++ b/src/components/Interaction/InfiniteScroll.tsx @@ -25,6 +25,8 @@ import { PullToRefresh, Spinner } from '~/components' */ interface Props { + loading?: boolean + /** * Does the resource have more entities */ @@ -47,6 +49,7 @@ interface Props { } export const InfiniteScroll: React.FC = ({ + loading, hasNextPage, loader = , loadMore, @@ -61,7 +64,15 @@ export const InfiniteScroll: React.FC = ({
{children} {hasNextPage && ( - + { + if (loading) { + return + } + + loadMore() + }} + > )} diff --git a/src/pages/icymi.tsx b/src/pages/icymi.tsx deleted file mode 100644 index c774a27cf9..0000000000 --- a/src/pages/icymi.tsx +++ /dev/null @@ -1,3 +0,0 @@ -import ArticleFeed from '~/views/ArticleFeed' - -export default () => diff --git a/src/pages/topics.tsx b/src/pages/topics.tsx deleted file mode 100644 index be1d47ba85..0000000000 --- a/src/pages/topics.tsx +++ /dev/null @@ -1,3 +0,0 @@ -import ArticleFeed from '~/views/ArticleFeed' - -export default () => diff --git a/src/views/ArticleFeed/index.tsx b/src/views/ArticleFeed/index.tsx deleted file mode 100644 index d4fa496465..0000000000 --- a/src/views/ArticleFeed/index.tsx +++ /dev/null @@ -1,155 +0,0 @@ -import { useQuery } from '@apollo/react-hooks' -import gql from 'graphql-tag' - -import { - ArticleDigestFeed, - EmptyArticle, - Head, - InfiniteScroll, - Layout, - List, - Spinner, -} from '~/components' -import { QueryError } from '~/components/GQL' - -import { analytics, mergeConnections } from '~/common/utils' - -import { AllIcymis } from './__generated__/AllIcymis' -import { AllTopics } from './__generated__/AllTopics' - -interface ArticleFeedProp { - type?: 'icymi' | 'topic' -} - -const QUERIES = { - topic: gql` - query AllTopics($after: String) { - viewer { - id - recommendation { - articles: topics(input: { first: 10, after: $after }) { - pageInfo { - startCursor - endCursor - hasNextPage - } - edges { - cursor - node { - ...ArticleDigestFeedArticlePublic - ...ArticleDigestFeedArticlePrivate - } - } - } - } - } - } - ${ArticleDigestFeed.fragments.article.public} - ${ArticleDigestFeed.fragments.article.private} - `, - icymi: gql` - query AllIcymis($after: String) { - viewer { - id - recommendation { - articles: icymi(input: { first: 10, after: $after }) { - pageInfo { - startCursor - endCursor - hasNextPage - } - edges { - cursor - node { - ...ArticleDigestFeedArticlePublic - ...ArticleDigestFeedArticlePrivate - } - } - } - } - } - } - ${ArticleDigestFeed.fragments.article.public} - ${ArticleDigestFeed.fragments.article.private} - `, -} - -const Feed = ({ type = 'topic' }: ArticleFeedProp) => { - const { data, loading, error, fetchMore, refetch } = useQuery< - AllTopics | AllIcymis - >(QUERIES[type]) - - if (loading) { - return - } - - if (error) { - return - } - - const connectionPath = 'viewer.recommendation.articles' - const { edges, pageInfo } = data?.viewer?.recommendation.articles || {} - - if (!edges || edges.length <= 0 || !pageInfo) { - return - } - - const loadMore = () => { - analytics.trackEvent('load_more', { - type: type === 'topic' ? 'all_topics' : 'all_icymi', - location: edges.length, - }) - return fetchMore({ - variables: { - after: pageInfo.endCursor, - }, - updateQuery: (previousResult, { fetchMoreResult }) => - mergeConnections({ - oldData: previousResult, - newData: fetchMoreResult, - path: connectionPath, - }), - }) - } - - return ( - - - {edges.map(({ node, cursor }, i) => ( - - - analytics.trackEvent('click_feed', { - type: type === 'topic' ? 'all_topics' : 'all_icymi', - contentType: 'article', - styleType: 'small_cover', - location: i, - }) - } - /> - - ))} - - - ) -} - -export default ({ type = 'topic' }: ArticleFeedProp) => ( - - } - right={ - - } - /> - - - - - -) diff --git a/src/views/Home/Feed/gql.ts b/src/views/Home/Feed/gql.ts new file mode 100644 index 0000000000..4b46925075 --- /dev/null +++ b/src/views/Home/Feed/gql.ts @@ -0,0 +1,103 @@ +import gql from 'graphql-tag' + +import { ArticleDigestFeed } from '~/components' + +const feedFragment = gql` + fragment FeedArticleConnection on ArticleConnection { + pageInfo { + startCursor + endCursor + hasNextPage + } + edges { + cursor + node { + ...ArticleDigestFeedArticlePublic + ...ArticleDigestFeedArticlePrivate + } + } + } + ${ArticleDigestFeed.fragments.article.public} + ${ArticleDigestFeed.fragments.article.private} +` + +export const FEED_ARTICLES_PUBLIC = { + hottest: gql` + query HottestFeedPublic($after: String) { + viewer { + id + recommendation { + feed: hottest(input: { first: 10, after: $after }) { + ...FeedArticleConnection + } + } + } + } + ${feedFragment} + `, + valued: gql` + query ValuedFeedPublic($after: String) { + viewer { + id + recommendation { + feed: valued(input: { first: 10, after: $after }) { + ...FeedArticleConnection + } + } + } + } + ${feedFragment} + `, + + newest: gql` + query NewestFeedPublic($after: String) { + viewer { + id + recommendation { + feed: newest(input: { first: 10, after: $after }) { + ...FeedArticleConnection + } + } + } + } + ${feedFragment} + `, + icymi: gql` + query IcymiFeedPublic($after: String) { + viewer { + id + recommendation { + feed: icymi(input: { first: 10, after: $after }) { + ...FeedArticleConnection + } + } + } + } + ${feedFragment} + `, + topics: gql` + query TopicsFeedPublic($after: String) { + viewer { + id + recommendation { + feed: topics(input: { first: 10, after: $after }) { + ...FeedArticleConnection + } + } + } + } + ${feedFragment} + `, +} + +export const FEED_ARTICLES_PRIVATE = gql` + query FeedArticlesPrivate($ids: [ID!]!) { + nodes(input: { ids: $ids }) { + id + ... on Article { + ...ArticleDigestFeedArticlePrivate + } + } + } + ${ArticleDigestFeed.fragments.article.private} +` diff --git a/src/views/Home/Feed/index.tsx b/src/views/Home/Feed/index.tsx index 4ee67bf9c5..f1bfdd0665 100644 --- a/src/views/Home/Feed/index.tsx +++ b/src/views/Home/Feed/index.tsx @@ -1,7 +1,6 @@ import { useQuery } from '@apollo/react-hooks' import { NetworkStatus } from 'apollo-client' -import gql from 'graphql-tag' -import { useContext } from 'react' +import { useContext, useEffect, useRef } from 'react' import { ArticleDigestFeed, @@ -18,199 +17,185 @@ import CLIENT_PREFERENCE from '~/components/GQL/queries/clientPreference' import { analytics, mergeConnections } from '~/common/utils' import Authors from './Authors' +import { FEED_ARTICLES_PRIVATE, FEED_ARTICLES_PUBLIC } from './gql' import SortBy, { SortByType } from './SortBy' import styles from './styles.css' import Tags from './Tags' import { ClientPreference } from '~/components/GQL/queries/__generated__/ClientPreference' import { - HottestFeed, - HottestFeed_viewer_recommendation_feed_edges, -} from './__generated__/HottestFeed' + HottestFeedPublic, + HottestFeedPublic_viewer_recommendation_feed_edges, +} from './__generated__/HottestFeedPublic' import { - NewestFeed, - NewestFeed_viewer_recommendation_feed_edges, -} from './__generated__/NewestFeed' + IcymiFeedPublic, + IcymiFeedPublic_viewer_recommendation_feed_edges, +} from './__generated__/IcymiFeedPublic' +import { + NewestFeedPublic, + NewestFeedPublic_viewer_recommendation_feed_edges, +} from './__generated__/NewestFeedPublic' +import { + TopicsFeedPublic, + TopicsFeedPublic_viewer_recommendation_feed_edges, +} from './__generated__/TopicsFeedPublic' +import { + ValuedFeedPublic, + ValuedFeedPublic_viewer_recommendation_feed_edges, +} from './__generated__/ValuedFeedPublic' + +type FeedArticlesPublic = + | HottestFeedPublic + | NewestFeedPublic + | IcymiFeedPublic + | ValuedFeedPublic + | TopicsFeedPublic type HorizontalFeed = React.FC<{ after?: string; first?: number }> -interface FeedEdge { +interface HorizontalFeedEdge { __typename: 'HorizontalFeed' Feed: HorizontalFeed } +type FeedEdge = + | HorizontalFeedEdge + | HottestFeedPublic_viewer_recommendation_feed_edges + | IcymiFeedPublic_viewer_recommendation_feed_edges + | NewestFeedPublic_viewer_recommendation_feed_edges + | TopicsFeedPublic_viewer_recommendation_feed_edges + | ValuedFeedPublic_viewer_recommendation_feed_edges + interface FeedLocation { [key: number]: HorizontalFeed } +interface MainFeedProps { + feedSortType: SortByType + viewMode: string | null +} + const horizontalFeeds: FeedLocation = { 2: () => , 5: () => , } -const feedFragment = gql` - fragment FeedArticleConnection on ArticleConnection { - pageInfo { - startCursor - endCursor - hasNextPage - } - edges { - cursor - node { - ...ArticleDigestFeedArticlePublic - ...ArticleDigestFeedArticlePrivate - } - } - } - ${ArticleDigestFeed.fragments.article.public} - ${ArticleDigestFeed.fragments.article.private} -` - -export const queries = { - hottest: gql` - query ValuedFeed($after: String) { - viewer { - id - recommendation { - feed: valued(input: { first: 10, after: $after }) { - ...FeedArticleConnection - } - } - } - } - ${feedFragment} - `, - newest: gql` - query NewestFeed($after: String) { - viewer { - id - recommendation { - feed: newest(input: { first: 10, after: $after }) { - ...FeedArticleConnection - } - } - } - } - ${feedFragment} - `, - icymi: gql` - query IcymiFeed($after: String) { - viewer { - id - recommendation { - feed: icymi(input: { first: 10, after: $after }) { - ...FeedArticleConnection - } - } - } - } - ${feedFragment} - `, - topics: gql` - query TopicsFeed($after: String) { - viewer { - id - recommendation { - feed: topics(input: { first: 10, after: $after }) { - ...FeedArticleConnection - } - } - } - } - ${feedFragment} - `, -} - -const MainFeed = ({ feedSortType: sortBy }: { feedSortType: SortByType }) => { +const MainFeed = ({ feedSortType: sortBy, viewMode }: MainFeedProps) => { + const viewer = useContext(ViewerContext) const isLargeUp = useResponsive('lg-up') const isHottestFeed = sortBy === 'hottest' - const viewer = useContext(ViewerContext) - + /** + * Data Fetching + */ // split out group b if in hottest feed and user is logged in - if (isHottestFeed && viewer.id && viewer.info.group === 'b') { - queries.hottest = gql` - query HottestFeed($after: String) { - viewer { - id - recommendation { - feed: hottest(input: { first: 10, after: $after }) { - ...FeedArticleConnection - } - } - } - } - ${feedFragment} - ` + if (isHottestFeed && (!viewer.id || viewer.info.group !== 'b')) { + FEED_ARTICLES_PUBLIC.hottest = FEED_ARTICLES_PUBLIC.valued } + // public data const { data, error, loading, - fetchMore: fetchMoreMainFeed, + fetchMore, networkStatus, refetch, - } = useQuery(queries[sortBy], { + client, + } = useQuery(FEED_ARTICLES_PUBLIC[sortBy], { notifyOnNetworkStatusChange: true, }) + // pagination const connectionPath = 'viewer.recommendation.feed' - const result = data?.viewer?.recommendation.feed - const { edges, pageInfo } = result || {} + const { edges, pageInfo } = data?.viewer?.recommendation.feed || {} const isNewLoading = networkStatus === NetworkStatus.loading - const { data: localCache } = useQuery(CLIENT_PREFERENCE, { - variables: { id: 'local' }, - }) + // private data + const loadPrivate = (publicData?: FeedArticlesPublic) => { + if (!viewer.id || !publicData) { + return + } - const { viewMode } = localCache?.clientPreference || { viewMode: 'default' } + const publiceEdges = publicData.viewer?.recommendation?.feed.edges || [] + const publicIds = publiceEdges.map(({ node }) => node.id) - if (loading && (!result || isNewLoading)) { - if (process.browser) { - window.scrollTo(0, 0) - document.body.focus() - } - return + client.query({ + query: FEED_ARTICLES_PRIVATE, + fetchPolicy: 'network-only', + variables: { ids: publicIds }, + }) } - if (error) { - return - } + // fetch private data for first page + const fetchedPrviateSortsRef = useRef([]) + useEffect(() => { + const fetched = fetchedPrviateSortsRef.current.indexOf(sortBy) >= 0 + if (loading || !edges || fetched) { + return + } - if (!edges || edges.length <= 0 || !pageInfo) { - return - } + loadPrivate(data) + fetchedPrviateSortsRef.current = [...fetchedPrviateSortsRef.current, sortBy] + }, [!!edges, loading, sortBy, viewer.id]) - const loadMore = () => { + // load next page + const loadMore = async () => { + console.log({ loading, isNewLoading }) analytics.trackEvent('load_more', { type: sortBy, - location: edges.length, + location: edges?.length || 0, }) - return fetchMoreMainFeed({ + + const { data: newData } = await fetchMore({ variables: { - after: pageInfo.endCursor, + after: pageInfo?.endCursor, }, // previousResult could be undefined when scrolling before loading finishes, reason unknown - updateQuery: (previousResult, { fetchMoreResult }) => - previousResult + updateQuery: (previousResult, { fetchMoreResult }) => { + console.log( + previousResult?.viewer?.recommendation.feed.edges?.map( + ({ node }) => node.id + ), + fetchMoreResult?.viewer?.recommendation.feed.edges?.map( + ({ node }) => node.id + ) + ) + return previousResult ? mergeConnections({ oldData: previousResult, newData: fetchMoreResult, path: connectionPath, dedupe: true, }) - : fetchMoreResult, + : fetchMoreResult + }, }) + + loadPrivate(newData) + } + + /** + * Render + */ + if (loading && isNewLoading) { + return + } + + if (error) { + return + } + + if (!edges || edges.length <= 0 || !pageInfo) { + return } // insert other feeds - let mixFeed: Array< - | FeedEdge - | HottestFeed_viewer_recommendation_feed_edges - | NewestFeed_viewer_recommendation_feed_edges - > = edges + let mixFeed: FeedEdge[] = edges + console.log( + edges.map(({ node }) => node.id), + pageInfo.endCursor + ) if (!isLargeUp && isHottestFeed) { // get copy @@ -233,6 +218,7 @@ const MainFeed = ({ feedSortType: sortBy }: { feedSortType: SortByType }) => { return ( { if (edge.__typename === 'HorizontalFeed') { const { Feed } = edge return - } else { - return ( - - - analytics.trackEvent('click_feed', { - type: sortBy, - styleType: - viewMode === 'default' - ? 'small_cover' - : viewMode === 'compact' - ? 'no_cover' - : 'large_cover', - contentType: 'article', - location: i, - }) - } - /> - - ) } + + return ( + + + analytics.trackEvent('click_feed', { + type: sortBy, + styleType: + viewMode === 'default' + ? 'small_cover' + : viewMode === 'compact' + ? 'no_cover' + : 'large_cover', + contentType: 'article', + location: i, + }) + } + /> + + ) })} @@ -274,6 +260,7 @@ const HomeFeed = () => { const { data, client } = useQuery(CLIENT_PREFERENCE, { variables: { id: 'local' }, }) + const { viewMode } = data?.clientPreference || { viewMode: 'default' } const { feedSortType } = data?.clientPreference || { feedSortType: 'hottest', } @@ -292,7 +279,7 @@ const HomeFeed = () => {
- + diff --git a/src/views/Home/SectionHeader/index.tsx b/src/views/Home/SectionHeader/index.tsx index fda2388d0e..c67597e84b 100644 --- a/src/views/Home/SectionHeader/index.tsx +++ b/src/views/Home/SectionHeader/index.tsx @@ -5,20 +5,16 @@ import { PATHS } from '~/common/enums' import styles from './styles.css' interface SidebarHeaderProps { - type: 'icymi' | 'authors' | 'tags' | 'topics' + type: 'authors' | 'tags' rightButton?: React.ReactNode } const FeedHeader = ({ type, rightButton }: SidebarHeaderProps) => { const pathMap = { - icymi: PATHS.ICYMI, - topics: PATHS.TOPICS, authors: PATHS.AUTHORS, tags: PATHS.TAGS, } const titleMap = { - icymi: , - topics: , authors: , tags: , } diff --git a/src/views/Me/Settings/Settings/UI/ViewMode.tsx b/src/views/Me/Settings/Settings/UI/ViewMode.tsx index 8048233833..9e8fdc5973 100644 --- a/src/views/Me/Settings/Settings/UI/ViewMode.tsx +++ b/src/views/Me/Settings/Settings/UI/ViewMode.tsx @@ -16,7 +16,7 @@ import { STORE_KEY_VIEW_MODE } from '~/common/enums' import { ClientPreference } from '~/components/GQL/queries/__generated__/ClientPreference' -type ViewMode = 'default' | 'comfortable' | 'compact' +export type ViewModeType = 'default' | 'comfortable' | 'compact' const ViewMode = () => { const { data, client } = useQuery(CLIENT_PREFERENCE, { @@ -27,7 +27,7 @@ const ViewMode = () => { const isComfortableMode = viewMode === 'comfortable' const isCompactMode = viewMode === 'compact' - const setViewMode = (mode: ViewMode) => { + const setViewMode = (mode: ViewModeType) => { if (client) { client.writeData({ id: 'ClientPreference:local', From ff6850d0fc8ecde80d4a34325deaf68cd6369ded Mon Sep 17 00:00:00 2001 From: RoberTu Date: Tue, 14 Jul 2020 22:07:46 +0800 Subject: [PATCH 38/86] feat(public-private-queries): `` & `` --- src/components/Interaction/InfiniteScroll.tsx | 13 +--- src/views/Home/Feed/Authors/gql.ts | 39 ++++++++++ src/views/Home/Feed/Authors/index.tsx | 73 +++++++++++------- src/views/Home/Feed/gql.ts | 1 - src/views/Home/Feed/index.tsx | 14 ---- src/views/Home/Sidebar/Authors/gql.ts | 38 ++++++++++ src/views/Home/Sidebar/Authors/index.tsx | 75 +++++++++++-------- 7 files changed, 168 insertions(+), 85 deletions(-) create mode 100644 src/views/Home/Feed/Authors/gql.ts create mode 100644 src/views/Home/Sidebar/Authors/gql.ts diff --git a/src/components/Interaction/InfiniteScroll.tsx b/src/components/Interaction/InfiniteScroll.tsx index 56074c09b8..c53c423642 100644 --- a/src/components/Interaction/InfiniteScroll.tsx +++ b/src/components/Interaction/InfiniteScroll.tsx @@ -25,8 +25,6 @@ import { PullToRefresh, Spinner } from '~/components' */ interface Props { - loading?: boolean - /** * Does the resource have more entities */ @@ -49,7 +47,6 @@ interface Props { } export const InfiniteScroll: React.FC = ({ - loading, hasNextPage, loader = , loadMore, @@ -64,15 +61,7 @@ export const InfiniteScroll: React.FC = ({
{children} {hasNextPage && ( - { - if (loading) { - return - } - - loadMore() - }} - > + )} diff --git a/src/views/Home/Feed/Authors/gql.ts b/src/views/Home/Feed/Authors/gql.ts new file mode 100644 index 0000000000..16d4e9def9 --- /dev/null +++ b/src/views/Home/Feed/Authors/gql.ts @@ -0,0 +1,39 @@ +import gql from 'graphql-tag' +import _chunk from 'lodash/chunk' + +import { UserDigest } from '~/components' + +export const FEED_AUTHORS_PUBLIC = gql` + query FeedAuthorsPublic { + viewer { + id + recommendation { + authors( + input: { first: 9, filter: { random: true, followed: false } } + ) { + edges { + cursor + node { + ...UserDigestRichUserPublic + ...UserDigestRichUserPrivate + } + } + } + } + } + } + ${UserDigest.Rich.fragments.user.public} + ${UserDigest.Rich.fragments.user.private} +` + +export const FEED_AUTHORS_PRIVATE = gql` + query FeedAuthorsPrivate($ids: [ID!]!) { + nodes(input: { ids: $ids }) { + id + ... on User { + ...UserDigestRichUserPrivate + } + } + } + ${UserDigest.Rich.fragments.user.private} +` diff --git a/src/views/Home/Feed/Authors/index.tsx b/src/views/Home/Feed/Authors/index.tsx index f5daa1379e..bd562d067c 100644 --- a/src/views/Home/Feed/Authors/index.tsx +++ b/src/views/Home/Feed/Authors/index.tsx @@ -1,6 +1,6 @@ import { useQuery } from '@apollo/react-hooks' -import gql from 'graphql-tag' import _chunk from 'lodash/chunk' +import { useContext, useEffect } from 'react' import { Button, @@ -10,45 +10,62 @@ import { TextIcon, Translate, UserDigest, + ViewerContext, } from '~/components' import { QueryError } from '~/components/GQL' import { analytics } from '~/common/utils' import SectionHeader from '../../SectionHeader' +import { FEED_AUTHORS_PRIVATE, FEED_AUTHORS_PUBLIC } from './gql' -import { FeedAuthors as FeedAuthorsType } from './__generated__/FeedAuthors' - -const FEED_AUTHORS = gql` - query FeedAuthors { - viewer { - id - recommendation { - authors( - input: { first: 9, filter: { random: true, followed: false } } - ) { - edges { - cursor - node { - ...UserDigestRichUserPublic - ...UserDigestRichUserPrivate - } - } - } - } - } - } - ${UserDigest.Rich.fragments.user.public} - ${UserDigest.Rich.fragments.user.private} -` +import { FeedAuthorsPublic } from './__generated__/FeedAuthorsPublic' const FeedAuthors = () => { - const { data, loading, error, refetch } = useQuery( - FEED_AUTHORS, + const viewer = useContext(ViewerContext) + + /** + * Data Fetching + */ + // public data + const { data, loading, error, refetch, client } = useQuery( + FEED_AUTHORS_PUBLIC, { notifyOnNetworkStatusChange: true } ) + const edges = data?.viewer?.recommendation.authors.edges + // private data + const loadPrivate = (publicData?: FeedAuthorsPublic) => { + if (!viewer.id || !publicData) { + return + } + + const publiceEdges = publicData?.viewer?.recommendation.authors.edges || [] + const publicIds = publiceEdges.map(({ node }) => node.id) + + client.query({ + query: FEED_AUTHORS_PRIVATE, + fetchPolicy: 'network-only', + variables: { ids: publicIds }, + }) + } + + // fetch private data for first page + useEffect(() => { + if (loading || !edges) { + return + } + + loadPrivate(data) + }, [!!edges, viewer.id]) + + // fetch + const suffle = async () => { + const { data: newData } = await refetch() + loadPrivate(newData) + } + if (error) { return } @@ -65,7 +82,7 @@ const FeedAuthors = () => { size={[null, '1.25rem']} spacing={[0, 'xtight']} bgActiveColor="grey-lighter" - onClick={() => refetch()} + onClick={suffle} > } diff --git a/src/views/Home/Feed/gql.ts b/src/views/Home/Feed/gql.ts index 4b46925075..e3a219cade 100644 --- a/src/views/Home/Feed/gql.ts +++ b/src/views/Home/Feed/gql.ts @@ -48,7 +48,6 @@ export const FEED_ARTICLES_PUBLIC = { } ${feedFragment} `, - newest: gql` query NewestFeedPublic($after: String) { viewer { diff --git a/src/views/Home/Feed/index.tsx b/src/views/Home/Feed/index.tsx index f1bfdd0665..1981348345 100644 --- a/src/views/Home/Feed/index.tsx +++ b/src/views/Home/Feed/index.tsx @@ -141,7 +141,6 @@ const MainFeed = ({ feedSortType: sortBy, viewMode }: MainFeedProps) => { // load next page const loadMore = async () => { - console.log({ loading, isNewLoading }) analytics.trackEvent('load_more', { type: sortBy, location: edges?.length || 0, @@ -153,14 +152,6 @@ const MainFeed = ({ feedSortType: sortBy, viewMode }: MainFeedProps) => { }, // previousResult could be undefined when scrolling before loading finishes, reason unknown updateQuery: (previousResult, { fetchMoreResult }) => { - console.log( - previousResult?.viewer?.recommendation.feed.edges?.map( - ({ node }) => node.id - ), - fetchMoreResult?.viewer?.recommendation.feed.edges?.map( - ({ node }) => node.id - ) - ) return previousResult ? mergeConnections({ oldData: previousResult, @@ -192,10 +183,6 @@ const MainFeed = ({ feedSortType: sortBy, viewMode }: MainFeedProps) => { // insert other feeds let mixFeed: FeedEdge[] = edges - console.log( - edges.map(({ node }) => node.id), - pageInfo.endCursor - ) if (!isLargeUp && isHottestFeed) { // get copy @@ -218,7 +205,6 @@ const MainFeed = ({ feedSortType: sortBy, viewMode }: MainFeedProps) => { return ( { - const { data, loading, error, refetch } = useQuery( - SIDEBAR_AUTHORS, - { notifyOnNetworkStatusChange: true } - ) + const viewer = useContext(ViewerContext) + + /** + * Data Fetching + */ + // public data + const { data, loading, error, refetch, client } = useQuery< + SidebarAuthorsPublic + >(SIDEBAR_AUTHORS_PUBLIC, { notifyOnNetworkStatusChange: true }) const edges = data?.viewer?.recommendation.authors.edges + // private data + const loadPrivate = (publicData?: SidebarAuthorsPublic) => { + if (!viewer.id || !publicData) { + return + } + + const publiceEdges = publicData?.viewer?.recommendation.authors.edges || [] + const publicIds = publiceEdges.map(({ node }) => node.id) + + client.query({ + query: SIDEBAR_AUTHORS_PRIVATE, + fetchPolicy: 'network-only', + variables: { ids: publicIds }, + }) + } + + // fetch private data for first page + useEffect(() => { + if (loading || !edges) { + return + } + + loadPrivate(data) + }, [!!edges, viewer.id]) + + // fetch + const suffle = async () => { + const { data: newData } = await refetch() + loadPrivate(newData) + } + if (error) { return } @@ -65,7 +80,7 @@ const Authors = () => { size={[null, '1.25rem']} spacing={[0, 'xtight']} bgActiveColor="grey-lighter" - onClick={() => refetch()} + onClick={suffle} > } From 6935789f6f9ce0fdd74af86658a43e3031d3451d Mon Sep 17 00:00:00 2001 From: Zeck Li Date: Tue, 14 Jul 2020 22:48:36 +0800 Subject: [PATCH 39/86] feat(component): redesign follow feed list item description and position --- src/views/Follow/FollowFeed/TagsFeed/index.tsx | 5 ++++- src/views/Follow/FollowFeed/TagsFeed/styles.css | 8 ++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/views/Follow/FollowFeed/TagsFeed/index.tsx b/src/views/Follow/FollowFeed/TagsFeed/index.tsx index 99e169029b..048033dade 100644 --- a/src/views/Follow/FollowFeed/TagsFeed/index.tsx +++ b/src/views/Follow/FollowFeed/TagsFeed/index.tsx @@ -12,6 +12,7 @@ import { List, Spinner, Tag, + Translate, } from '~/components' import { QueryError } from '~/components/GQL' @@ -135,6 +136,9 @@ const TagsArticles = ({ tagIds }: { tagIds: string[] }) => { return (
+ + +
) @@ -160,7 +164,6 @@ const TagsArticles = ({ tagIds }: { tagIds: string[] }) => { location: i, }) } - inFollowFeed /> ))} diff --git a/src/views/Follow/FollowFeed/TagsFeed/styles.css b/src/views/Follow/FollowFeed/TagsFeed/styles.css index 4768230f38..fb78df594b 100644 --- a/src/views/Follow/FollowFeed/TagsFeed/styles.css +++ b/src/views/Follow/FollowFeed/TagsFeed/styles.css @@ -1,4 +1,12 @@ .tag { + @mixin flex-center-start; + width: 100%; margin: var(--spacing-base) 0 var(--spacing-xx-tight) var(--spacing-base); + + & span { + margin-left: var(--spacing-x-tight); + font-size: var(--font-size-sm); + color: var(--color-grey); + } } From 1c37b0ebf11077e1afdd2e64b9283110ce5f7e43 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 14 Jul 2020 22:22:23 +0000 Subject: [PATCH 40/86] build(deps): bump formik from 2.1.4 to 2.1.5 Bumps [formik](https://github.com/formik/formik) from 2.1.4 to 2.1.5. - [Release notes](https://github.com/formik/formik/releases) - [Commits](https://github.com/formik/formik/compare/v2.1.4...v2.1.5) Signed-off-by: dependabot-preview[bot] --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 244f7ee2c8..351bfa384f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12771,9 +12771,9 @@ } }, "formik": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/formik/-/formik-2.1.4.tgz", - "integrity": "sha512-oKz8S+yQBzuQVSEoxkqqJrKQS5XJASWGVn6mrs+oTWrBoHgByVwwI1qHiVc9GKDpZBU9vAxXYAKz2BvujlwunA==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/formik/-/formik-2.1.5.tgz", + "integrity": "sha512-bWpo3PiqVDYslvrRjTq0Isrm0mFXHiO33D8MS6t6dWcqSFGeYF52nlpCM2xwOJ6tRVRznDkL+zz/iHPL4LDuvQ==", "requires": { "deepmerge": "^2.1.1", "hoist-non-react-statics": "^3.3.0", diff --git a/package.json b/package.json index 92eaf05110..eaf0136edf 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "express": "^4.17.1", "fingerprintjs2": "^2.1.0", "firebase": "^7.16.0", - "formik": "^2.1.4", + "formik": "^2.1.5", "graphql": "^14.7.0", "graphql-tag": "^2.10.4", "helmet": "^3.23.3", From 86d45b1154a86bbdd3bfa15f02bf0d3896c3785e Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 14 Jul 2020 22:24:18 +0000 Subject: [PATCH 41/86] build(deps-dev): bump apollo from 2.29.1 to 2.30.0 Bumps [apollo](https://github.com/apollographql/apollo-tooling) from 2.29.1 to 2.30.0. - [Release notes](https://github.com/apollographql/apollo-tooling/releases) - [Changelog](https://github.com/apollographql/apollo-tooling/blob/master/CHANGELOG.md) - [Commits](https://github.com/apollographql/apollo-tooling/compare/apollo@2.29.1...apollo@2.30.0) Signed-off-by: dependabot-preview[bot] --- package-lock.json | 72 ++++++++++++++++++++++------------------------- package.json | 2 +- 2 files changed, 34 insertions(+), 40 deletions(-) diff --git a/package-lock.json b/package-lock.json index 244f7ee2c8..f8ee77f050 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6695,9 +6695,9 @@ } }, "apollo": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/apollo/-/apollo-2.29.1.tgz", - "integrity": "sha512-2wobALKlgjQsSPEjP4jN+ceH7DnZdOkcY28owNP6v1hdPhsznOdc0AYYvR9dMlb+NzcBqTOlqJekFUkzfM00Ig==", + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/apollo/-/apollo-2.30.0.tgz", + "integrity": "sha512-lavRq0n2wmpKZpoW0p4nclzKFDuFJfV9MaC12rb0HOV2lMMBxVt2MJj2YLZzlsTIyZiK34Q5pYFx17plIqKQyA==", "dev": true, "requires": { "@apollographql/apollo-tools": "^0.4.8", @@ -6709,14 +6709,14 @@ "@oclif/plugin-not-found": "1.2.4", "@oclif/plugin-plugins": "1.9.0", "@oclif/plugin-warn-if-update-available": "1.7.0", - "apollo-codegen-core": "^0.37.5", - "apollo-codegen-flow": "^0.35.5", - "apollo-codegen-scala": "^0.36.5", - "apollo-codegen-swift": "^0.37.5", - "apollo-codegen-typescript": "^0.37.5", + "apollo-codegen-core": "^0.37.6", + "apollo-codegen-flow": "^0.35.6", + "apollo-codegen-scala": "^0.36.6", + "apollo-codegen-swift": "^0.37.6", + "apollo-codegen-typescript": "^0.37.6", "apollo-env": "^0.6.5", "apollo-graphql": "^0.5.0", - "apollo-language-server": "^1.23.1", + "apollo-language-server": "^1.23.2", "chalk": "2.4.2", "cli-ux": "5.4.9", "env-ci": "3.2.2", @@ -6726,7 +6726,7 @@ "git-url-parse": "^11.1.2", "glob": "7.1.5", "graphql": "14.0.2 - 14.2.0 || ^14.3.1 || ^15.0.0", - "graphql-tag": "2.10.3", + "graphql-tag": "2.10.4", "listr": "0.14.3", "lodash.identity": "3.0.0", "lodash.pickby": "4.6.0", @@ -6758,12 +6758,6 @@ "path-is-absolute": "^1.0.0" } }, - "graphql-tag": { - "version": "2.10.3", - "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.10.3.tgz", - "integrity": "sha512-4FOv3ZKfA4WdOKJeHdz6B3F/vxBLSgmBcGeAFPf4n1F64ltJUvOOerNj0rsJxONQGdhUMynQIvd6LzB+1J5oKA==", - "dev": true - }, "mkdirp": { "version": "0.5.5", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", @@ -6826,16 +6820,16 @@ } }, "apollo-codegen-core": { - "version": "0.37.5", - "resolved": "https://registry.npmjs.org/apollo-codegen-core/-/apollo-codegen-core-0.37.5.tgz", - "integrity": "sha512-mCfizUDuYsHQlZOwizc5Z2YRlvTakvQDjpDDQUAHW2ULMaCAnRSFEo+ksN+e1VdR4fAOZ1cEtCmIZ+HqpL2Bpw==", + "version": "0.37.6", + "resolved": "https://registry.npmjs.org/apollo-codegen-core/-/apollo-codegen-core-0.37.6.tgz", + "integrity": "sha512-n379jZas7Gwc3RdKHyp0oqAWAuBTWY2sbyqItkDq/n4qorbWpl7veLYwiLtnyIDBNghDbgnv5tDD9ukFy7459A==", "dev": true, "requires": { "@babel/generator": "7.10.4", "@babel/parser": "^7.1.3", "@babel/types": "7.10.4", "apollo-env": "^0.6.5", - "apollo-language-server": "^1.23.1", + "apollo-language-server": "^1.23.2", "ast-types": "^0.13.0", "common-tags": "^1.5.1", "recast": "^0.19.0" @@ -6879,14 +6873,14 @@ } }, "apollo-codegen-flow": { - "version": "0.35.5", - "resolved": "https://registry.npmjs.org/apollo-codegen-flow/-/apollo-codegen-flow-0.35.5.tgz", - "integrity": "sha512-1/fPPehm758yFuZIcMWKbYjJtapa6Pwuqo0broYKBSQ9lp42LSvbCfwtANktBnmxrFmlIFPFrBOnsjrz2eW+5w==", + "version": "0.35.6", + "resolved": "https://registry.npmjs.org/apollo-codegen-flow/-/apollo-codegen-flow-0.35.6.tgz", + "integrity": "sha512-alPIGkbhhTq97Leyx5RaneMEK3S2Ol2GghkH0ztEQRbglGx5Pibh0RZZNdDbOoYg/26umTlR1wQGPrJ9325fgw==", "dev": true, "requires": { "@babel/generator": "7.10.4", "@babel/types": "7.10.4", - "apollo-codegen-core": "^0.37.5", + "apollo-codegen-core": "^0.37.6", "change-case": "^4.0.0", "common-tags": "^1.5.1", "inflected": "^2.0.3" @@ -6930,38 +6924,38 @@ } }, "apollo-codegen-scala": { - "version": "0.36.5", - "resolved": "https://registry.npmjs.org/apollo-codegen-scala/-/apollo-codegen-scala-0.36.5.tgz", - "integrity": "sha512-QCtD1rV5OtYjlxiLJ7rfrRxALNRYUwYdlxXiyqfZrIRqtpGVHwPp/nDqFF0bYUIT135vj7dXz7xzzlvK5x+z6A==", + "version": "0.36.6", + "resolved": "https://registry.npmjs.org/apollo-codegen-scala/-/apollo-codegen-scala-0.36.6.tgz", + "integrity": "sha512-v83JJEy62w20jTa5RfPYtPbLMi5RmrGpebPMBkiZoSn1MY80KvMoB9sevOsHc5OAb1N5PuKxHlJgD8JKnxeNeQ==", "dev": true, "requires": { - "apollo-codegen-core": "^0.37.5", + "apollo-codegen-core": "^0.37.6", "change-case": "^4.0.0", "common-tags": "^1.5.1", "inflected": "^2.0.3" } }, "apollo-codegen-swift": { - "version": "0.37.5", - "resolved": "https://registry.npmjs.org/apollo-codegen-swift/-/apollo-codegen-swift-0.37.5.tgz", - "integrity": "sha512-5WdO4IEA/tvEhnbBbbMYujUT/L65Dy3LNS88s/Wa5MkgG0g603wRgFsFOETFXiBiPIVguCszKIqFcK/uGBfsYQ==", + "version": "0.37.6", + "resolved": "https://registry.npmjs.org/apollo-codegen-swift/-/apollo-codegen-swift-0.37.6.tgz", + "integrity": "sha512-45Vjh1blPpKSyknK8tRpJ9Scrh/SYfTi9qqZkT4T51iyRBbQFyiDDidrdaTepU609UwAInt0Q2hwqMZCWoT6ng==", "dev": true, "requires": { - "apollo-codegen-core": "^0.37.5", + "apollo-codegen-core": "^0.37.6", "change-case": "^4.0.0", "common-tags": "^1.5.1", "inflected": "^2.0.3" } }, "apollo-codegen-typescript": { - "version": "0.37.5", - "resolved": "https://registry.npmjs.org/apollo-codegen-typescript/-/apollo-codegen-typescript-0.37.5.tgz", - "integrity": "sha512-vfyr2P1ywUedepc410TwaX/+4M4KT/GOBWWL+fbeicWnxlecsDz5BMbD94nahRsvP8Iqd6ZIp3oxq4UeCSZbHQ==", + "version": "0.37.6", + "resolved": "https://registry.npmjs.org/apollo-codegen-typescript/-/apollo-codegen-typescript-0.37.6.tgz", + "integrity": "sha512-GhcPDNJgsCcVvrO4qKedJqGl/ALyRkTpc5KDQ2597SuQZDAmwVe5JpDePCU1HUsaqt1AsCiBSHu29AQCJFgxgg==", "dev": true, "requires": { "@babel/generator": "7.10.4", "@babel/types": "7.10.4", - "apollo-codegen-core": "^0.37.5", + "apollo-codegen-core": "^0.37.6", "change-case": "^4.0.0", "common-tags": "^1.5.1", "inflected": "^2.0.3" @@ -7051,9 +7045,9 @@ } }, "apollo-language-server": { - "version": "1.23.1", - "resolved": "https://registry.npmjs.org/apollo-language-server/-/apollo-language-server-1.23.1.tgz", - "integrity": "sha512-tP+B4jvs5KO/l1HYy4V6S302ypbmkyyDaWheQzlu0KStO9hlECDWk67nInmvKbRQ1Rov9oHDkujLADvH3pJkBA==", + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/apollo-language-server/-/apollo-language-server-1.23.2.tgz", + "integrity": "sha512-2EfnA0DUVhGk018XYPb44EM+KuBnAqdciRD+j9BuUT3+nVq7pc8pjjcS7M8r5ea8hnOYrAoxC6f4I2YNdhjHJg==", "dev": true, "requires": { "@apollo/federation": "0.17.0", diff --git a/package.json b/package.json index 92eaf05110..c28b4ad7cc 100644 --- a/package.json +++ b/package.json @@ -111,7 +111,7 @@ "@types/styled-jsx": "^2.2.8", "@types/validator": "^13.1.0", "@zeit/next-bundle-analyzer": "^0.1.2", - "apollo": "^2.29.1", + "apollo": "^2.30.0", "babel-jest": "^26.1.0", "babel-plugin-dynamic-import-node": "^2.3.3", "babel-polyfill": "^6.26.0", From d623e5716ebbce336d3d76eb926eddffe84da8a5 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 14 Jul 2020 22:25:32 +0000 Subject: [PATCH 42/86] build(deps): bump @sentry/browser from 5.19.1 to 5.19.2 Bumps [@sentry/browser](https://github.com/getsentry/sentry-javascript) from 5.19.1 to 5.19.2. - [Release notes](https://github.com/getsentry/sentry-javascript/releases) - [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-javascript/compare/5.19.1...5.19.2) Signed-off-by: dependabot-preview[bot] --- package-lock.json | 62 +++++++++++++++++++++++------------------------ package.json | 2 +- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/package-lock.json b/package-lock.json index 244f7ee2c8..56871f0f49 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4287,59 +4287,59 @@ } }, "@sentry/browser": { - "version": "5.19.1", - "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-5.19.1.tgz", - "integrity": "sha512-Aon5Nc2n8sIXKg6Xbr4RM3/Xs7vFpXksL56z3yIuGrmpCM8ToQ25/tQv8h+anYi72x5bn1npzaXB/NwU1Qwfhg==", + "version": "5.19.2", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-5.19.2.tgz", + "integrity": "sha512-o6Z532n+0N5ANDzgR9GN+Q6CU7zVlIJvBEW234rBiB+ZZj6XwTLS1dD+JexGr8lCo8PeXI2rypKcj1jUGLVW8w==", "requires": { - "@sentry/core": "5.19.1", - "@sentry/types": "5.19.1", - "@sentry/utils": "5.19.1", + "@sentry/core": "5.19.2", + "@sentry/types": "5.19.2", + "@sentry/utils": "5.19.2", "tslib": "^1.9.3" } }, "@sentry/core": { - "version": "5.19.1", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.19.1.tgz", - "integrity": "sha512-BGGxjeT95Og/hloBhQXAVcndVXPmIU6drtF3oKRT12cBpiG965xEDEUwiJVvyb5MAvojdVEZBK2LURUFY/d7Zw==", - "requires": { - "@sentry/hub": "5.19.1", - "@sentry/minimal": "5.19.1", - "@sentry/types": "5.19.1", - "@sentry/utils": "5.19.1", + "version": "5.19.2", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.19.2.tgz", + "integrity": "sha512-sfbBsVXpA0WYJUichz5IhvqKD8xJUfQvsszrTsUKa7PQAMAboOmuh6bo8KquaVQnAZyZWZU08UduvlSV3tA7tw==", + "requires": { + "@sentry/hub": "5.19.2", + "@sentry/minimal": "5.19.2", + "@sentry/types": "5.19.2", + "@sentry/utils": "5.19.2", "tslib": "^1.9.3" } }, "@sentry/hub": { - "version": "5.19.1", - "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.19.1.tgz", - "integrity": "sha512-XjfbNGWVeDsP38alm5Cm08YPIw5Hu6HbPkw7a3y1piViTrg4HdtsE+ZJqq0YcURo2RTpg6Ks6coCS/zJxIPygQ==", + "version": "5.19.2", + "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.19.2.tgz", + "integrity": "sha512-2KkEYX4q9TDCOiaVEo2kQ1W0IXyZxJxZtIjDdFQyes9T4ubYlKHAbvCjTxHSQv37lDO4t7sOIApWG9rlkHzlEA==", "requires": { - "@sentry/types": "5.19.1", - "@sentry/utils": "5.19.1", + "@sentry/types": "5.19.2", + "@sentry/utils": "5.19.2", "tslib": "^1.9.3" } }, "@sentry/minimal": { - "version": "5.19.1", - "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.19.1.tgz", - "integrity": "sha512-pgNfsaCroEsC8gv+NqmPTIkj4wyK6ZgYLV12IT4k2oJLkGyg45TSAKabyB7oEP5jsj8sRzm8tDomu8M4HpaCHg==", + "version": "5.19.2", + "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.19.2.tgz", + "integrity": "sha512-rApEOkjy+ZmkeqEItgFvUFxe5l+dht9AumuUzq74pWp+HJqxxv9IVTusKppBsE1adjtmyhwK4O3Wr8qyc75xlw==", "requires": { - "@sentry/hub": "5.19.1", - "@sentry/types": "5.19.1", + "@sentry/hub": "5.19.2", + "@sentry/types": "5.19.2", "tslib": "^1.9.3" } }, "@sentry/types": { - "version": "5.19.1", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.19.1.tgz", - "integrity": "sha512-M5MhTLnjqYFwxMwcFPBpBgYQqI9hCvtVuj/A+NvcBHpe7VWOXdn/Sys+zD6C76DWGFYQdw3OWCsZimP24dL8mA==" + "version": "5.19.2", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.19.2.tgz", + "integrity": "sha512-O6zkW8oM1qK5Uma9+B/UMlmlm9/gkw9MooqycWuEhIaKfDBj/yVbwb/UTiJmNkGc5VJQo0v1uXUZZQt6/Xq1GA==" }, "@sentry/utils": { - "version": "5.19.1", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.19.1.tgz", - "integrity": "sha512-neUiNBnZSHjWTZWy2QV02EHTx1C2L3DBPzRXlh0ca5xrI7LMBLmhkHlhebn1E5ky3PW1teqZTgmh0jZoL99TEA==", + "version": "5.19.2", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.19.2.tgz", + "integrity": "sha512-gEPkC0CJwvIWqcTcPSdIzqJkJa9N5vZzUZyBvdu1oiyJu7MfazpJEvj3whfJMysSfXJQxoJ+a1IPrA73VY23VA==", "requires": { - "@sentry/types": "5.19.1", + "@sentry/types": "5.19.2", "tslib": "^1.9.3" } }, diff --git a/package.json b/package.json index 92eaf05110..9d5a65a82b 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "@reach/alert": "^0.10.5", "@reach/dialog": "^0.10.5", "@reach/visually-hidden": "^0.10.4", - "@sentry/browser": "^5.19.1", + "@sentry/browser": "^5.19.2", "@stripe/react-stripe-js": "^1.1.2", "@stripe/stripe-js": "^1.8.0", "@tippyjs/react": "^4.0.4", From 189e2e67e5a03f7cbd5f1b37b20cf10ea99f445b Mon Sep 17 00:00:00 2001 From: Guo Liu Date: Tue, 14 Jul 2020 20:56:35 -0700 Subject: [PATCH 43/86] fix(analytics): fix view_donation_dialog event name --- src/common/utils/analytics.ts | 1 + src/components/Dialogs/DonationDialog/index.tsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/common/utils/analytics.ts b/src/common/utils/analytics.ts index 78ca76abd8..c58e7eec84 100644 --- a/src/common/utils/analytics.ts +++ b/src/common/utils/analytics.ts @@ -26,6 +26,7 @@ type EventArgs = | ['share', ShareProp] | ['purchase', PurchaseProp] | ['view_add_credit_dialog', ViewDialogProp] + | ['view_donation_dialog', ViewDialogProp] type ClickFeedProp = | ArticleFeedProp diff --git a/src/components/Dialogs/DonationDialog/index.tsx b/src/components/Dialogs/DonationDialog/index.tsx index 852231b137..927b76b6b2 100644 --- a/src/components/Dialogs/DonationDialog/index.tsx +++ b/src/components/Dialogs/DonationDialog/index.tsx @@ -187,7 +187,7 @@ const BaseDonationDialog = ({ const isHKD = currency === CURRENCY.HKD useEffect(() => { - analytics.trackEvent('view_add_credit_dialog', { step }) + analytics.trackEvent('view_donation_dialog', { step }) }, [step]) return ( From 72f7e086676654ceda2f3b7e862a0a0f1a928ebb Mon Sep 17 00:00:00 2001 From: Guo Liu Date: Tue, 14 Jul 2020 21:09:52 -0700 Subject: [PATCH 44/86] fix(article read): record read without 5s wait --- src/views/ArticleDetail/Content/index.tsx | 67 ++++++++++++----------- 1 file changed, 36 insertions(+), 31 deletions(-) diff --git a/src/views/ArticleDetail/Content/index.tsx b/src/views/ArticleDetail/Content/index.tsx index 9707e17a14..3cd8568e4c 100644 --- a/src/views/ArticleDetail/Content/index.tsx +++ b/src/views/ArticleDetail/Content/index.tsx @@ -56,41 +56,46 @@ const Content = ({ // register read useEffect(() => { - const timerId = setInterval(() => { - const isReading = () => { - // tab hidden - if (document.hidden) { - return false + const timerId = setInterval( + (function heartbeat() { + const isReading = () => { + // tab hidden + if (document.hidden) { + return false + } + + // idle for more than 5 minutes + if (Date.now() / 1000 - lastScroll > 60 * 5) { + return false + } + + if (!contentContainer || !contentContainer.current) { + return false + } + + // if overlay is shown + const overlaySelectors = ['reach-portal', '.tippy-popper'] + if (document.querySelector(overlaySelectors.join(','))) { + return false + } + + // if bottom is above center + const { + bottom, + } = ((contentContainer.current as unknown) as Element).getBoundingClientRect() + + const isBottomAboveCenter = bottom <= window.innerHeight / 2 + return !isBottomAboveCenter } - // idle for more than 5 minutes - if (Date.now() / 1000 - lastScroll > 60 * 5) { - return false + if (isReading()) { + read({ variables: { id } }) } - if (!contentContainer || !contentContainer.current) { - return false - } - - // if overlay is shown - const overlaySelectors = ['reach-portal', '.tippy-popper'] - if (document.querySelector(overlaySelectors.join(','))) { - return false - } - - // if bottom is above center - const { - bottom, - } = ((contentContainer.current as unknown) as Element).getBoundingClientRect() - - const isBottomAboveCenter = bottom <= window.innerHeight / 2 - return !isBottomAboveCenter - } - - if (isReading()) { - read({ variables: { id } }) - } - }, 5000) + return heartbeat + })(), + 5000 + ) // clean timer return () => { From b8542edc0b3c99f75421f8145fcc1261534e9793 Mon Sep 17 00:00:00 2001 From: Zeck Li Date: Wed, 15 Jul 2020 15:28:29 +0800 Subject: [PATCH 45/86] feat(component): add query filter for search APIs --- src/components/Form/DropdownInput/index.tsx | 4 +++- src/components/GQL/queries/searchArticles.ts | 4 ++-- .../TagDetail/Buttons/AddButton/TagArticleDialog/Content.tsx | 3 +++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/components/Form/DropdownInput/index.tsx b/src/components/Form/DropdownInput/index.tsx index 9614a22bc2..8411a53c47 100644 --- a/src/components/Form/DropdownInput/index.tsx +++ b/src/components/Form/DropdownInput/index.tsx @@ -16,6 +16,7 @@ interface DropdownProps { dropdownCallback?: (params: any) => void dropdownZIndex?: number query: any + queryFilter?: Record } type InputProps = { @@ -44,6 +45,7 @@ const DropdownInput: React.FC = ({ dropdownCallback, dropdownZIndex, query, + queryFilter, ...inputProps }) => { @@ -71,7 +73,7 @@ const DropdownInput: React.FC = ({ } const { data, loading } = useQuery(query, { - variables: { search: debouncedSearch }, + variables: { search: debouncedSearch, filter: queryFilter }, skip: !debouncedSearch, }) diff --git a/src/components/GQL/queries/searchArticles.ts b/src/components/GQL/queries/searchArticles.ts index 2c0f3e6041..05f4223a7f 100644 --- a/src/components/GQL/queries/searchArticles.ts +++ b/src/components/GQL/queries/searchArticles.ts @@ -3,8 +3,8 @@ import gql from 'graphql-tag' import { ArticleDigestDropdown } from '~/components' export default gql` - query SearchArticles($search: String!) { - search(input: { key: $search, type: Article, first: 5 }) { + query SearchArticles($search: String!, $filter: SearchFilter) { + search(input: { key: $search, type: Article, first: 5, filter: $filter }) { edges { node { ... on Article { diff --git a/src/views/TagDetail/Buttons/AddButton/TagArticleDialog/Content.tsx b/src/views/TagDetail/Buttons/AddButton/TagArticleDialog/Content.tsx index 20e9081725..d850b71752 100644 --- a/src/views/TagDetail/Buttons/AddButton/TagArticleDialog/Content.tsx +++ b/src/views/TagDetail/Buttons/AddButton/TagArticleDialog/Content.tsx @@ -12,6 +12,7 @@ import { LanguageContext, Translate, useResponsive, + ViewerContext, } from '~/components' import { useMutation } from '~/components/GQL' import SEARCH_ARTICLES from '~/components/GQL/queries/searchArticles' @@ -81,6 +82,7 @@ const TagArticleDialogContent: React.FC = ({ const [selectedArticles, setSelectedArticles] = useState([]) const [add] = useMutation(ADD_ARTICLES_TAGS) const { lang } = useContext(LanguageContext) + const viewer = useContext(ViewerContext) const formId = 'put-article-tag-form' @@ -199,6 +201,7 @@ const TagArticleDialogContent: React.FC = ({ dropdownCallback={onClickMenuItem} DropdownContent={DropdownContent} query={SEARCH_ARTICLES} + queryFilter={{ authorId: viewer.id }} />
    From 9e223c13c2212e46fcbbe766e566e806ceb8e598 Mon Sep 17 00:00:00 2001 From: RoberTu Date: Wed, 15 Jul 2020 15:45:26 +0800 Subject: [PATCH 46/86] fix(public-private-queries): skip loadMore if it's already in loading --- src/components/Interaction/InfiniteScroll.tsx | 13 +++------ src/views/Home/Feed/index.tsx | 27 ++++++++++--------- 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/src/components/Interaction/InfiniteScroll.tsx b/src/components/Interaction/InfiniteScroll.tsx index 74ab2abe79..a7b4780ae8 100644 --- a/src/components/Interaction/InfiniteScroll.tsx +++ b/src/components/Interaction/InfiniteScroll.tsx @@ -52,19 +52,13 @@ export const InfiniteScroll: React.FC = ({ pullToRefresh, children, }) => { - const Loader = () => ( - <> - - {loader} - - ) - if (pullToRefresh) { return ( <> {children} - {hasNextPage && } + {hasNextPage && } + {hasNextPage && loader} ) @@ -73,7 +67,8 @@ export const InfiniteScroll: React.FC = ({ return ( <> {children} - {hasNextPage && } + {hasNextPage && } + {hasNextPage && loader} ) } diff --git a/src/views/Home/Feed/index.tsx b/src/views/Home/Feed/index.tsx index 1981348345..254f5d00ae 100644 --- a/src/views/Home/Feed/index.tsx +++ b/src/views/Home/Feed/index.tsx @@ -108,7 +108,8 @@ const MainFeed = ({ feedSortType: sortBy, viewMode }: MainFeedProps) => { // pagination const connectionPath = 'viewer.recommendation.feed' - const { edges, pageInfo } = data?.viewer?.recommendation.feed || {} + const result = data?.viewer?.recommendation.feed + const { edges, pageInfo } = result || {} const isNewLoading = networkStatus === NetworkStatus.loading // private data @@ -141,6 +142,10 @@ const MainFeed = ({ feedSortType: sortBy, viewMode }: MainFeedProps) => { // load next page const loadMore = async () => { + if (loading || isNewLoading) { + return + } + analytics.trackEvent('load_more', { type: sortBy, location: edges?.length || 0, @@ -150,17 +155,13 @@ const MainFeed = ({ feedSortType: sortBy, viewMode }: MainFeedProps) => { variables: { after: pageInfo?.endCursor, }, - // previousResult could be undefined when scrolling before loading finishes, reason unknown - updateQuery: (previousResult, { fetchMoreResult }) => { - return previousResult - ? mergeConnections({ - oldData: previousResult, - newData: fetchMoreResult, - path: connectionPath, - dedupe: true, - }) - : fetchMoreResult - }, + updateQuery: (previousResult, { fetchMoreResult }) => + mergeConnections({ + oldData: previousResult, + newData: fetchMoreResult, + path: connectionPath, + dedupe: true, + }), }) loadPrivate(newData) @@ -169,7 +170,7 @@ const MainFeed = ({ feedSortType: sortBy, viewMode }: MainFeedProps) => { /** * Render */ - if (loading && isNewLoading) { + if (loading && (!result || isNewLoading)) { return } From 265caa697ab4636cf55fcdf7d1247abb26cb0a61 Mon Sep 17 00:00:00 2001 From: RoberTu Date: Wed, 15 Jul 2020 16:06:20 +0800 Subject: [PATCH 47/86] feat(public-private-queries): `` --- src/views/Authors/gql.ts | 41 ++++++++++++++ src/views/Authors/index.tsx | 108 ++++++++++++++++++++---------------- 2 files changed, 101 insertions(+), 48 deletions(-) create mode 100644 src/views/Authors/gql.ts diff --git a/src/views/Authors/gql.ts b/src/views/Authors/gql.ts new file mode 100644 index 0000000000..29e9937849 --- /dev/null +++ b/src/views/Authors/gql.ts @@ -0,0 +1,41 @@ +import gql from 'graphql-tag' + +import { UserDigest } from '~/components' + +export const ALL_AUTHORS_PUBLIC = gql` + query AllAuthorsPublic($after: String) { + viewer { + id + recommendation { + authors(input: { first: 20, after: $after }) { + pageInfo { + startCursor + endCursor + hasNextPage + } + edges { + cursor + node { + ...UserDigestRichUserPublic + ...UserDigestRichUserPrivate + } + } + } + } + } + } + ${UserDigest.Rich.fragments.user.public} + ${UserDigest.Rich.fragments.user.private} +` + +export const ALL_AUTHORS_PRIVATE = gql` + query AllAuthorsPrivate($ids: [ID!]!) { + nodes(input: { ids: $ids }) { + id + ... on User { + ...UserDigestRichUserPrivate + } + } + } + ${UserDigest.Rich.fragments.user.private} +` diff --git a/src/views/Authors/index.tsx b/src/views/Authors/index.tsx index 8b48481dc2..263e276395 100644 --- a/src/views/Authors/index.tsx +++ b/src/views/Authors/index.tsx @@ -1,5 +1,5 @@ import { useQuery } from '@apollo/react-hooks' -import gql from 'graphql-tag' +import { useContext, useEffect } from 'react' import { EmptyWarning, @@ -10,71 +10,62 @@ import { Spinner, Translate, UserDigest, + ViewerContext, } from '~/components' import { QueryError } from '~/components/GQL' import { analytics, mergeConnections } from '~/common/utils' -import { AllAuthors } from './__generated__/AllAuthors' - -const ALL_AUTHORSS = gql` - query AllAuthors($after: String) { - viewer { - id - recommendation { - authors(input: { first: 20, after: $after }) { - pageInfo { - startCursor - endCursor - hasNextPage - } - edges { - cursor - node { - ...UserDigestRichUserPublic - ...UserDigestRichUserPrivate - } - } - } - } - } - } - ${UserDigest.Rich.fragments.user.public} - ${UserDigest.Rich.fragments.user.private} -` +import { ALL_AUTHORS_PRIVATE, ALL_AUTHORS_PUBLIC } from './gql' -const Authors = () => { - const { data, loading, error, fetchMore, refetch } = useQuery( - ALL_AUTHORSS - ) +import { AllAuthorsPublic } from './__generated__/AllAuthorsPublic' - if (loading) { - return - } +const Authors = () => { + const viewer = useContext(ViewerContext) - if (error) { - return - } + /** + * Data Fetching + */ + // public data + const { data, loading, error, fetchMore, refetch, client } = useQuery< + AllAuthorsPublic + >(ALL_AUTHORS_PUBLIC) + // pagination const connectionPath = 'viewer.recommendation.authors' const { edges, pageInfo } = data?.viewer?.recommendation.authors || {} - if (!edges || edges.length <= 0 || !pageInfo) { - return ( - } - /> - ) + // private data + const loadPrivate = (publicData?: AllAuthorsPublic) => { + if (!viewer.id || !publicData) { + return + } + + const publiceEdges = publicData?.viewer?.recommendation.authors.edges || [] + const publicIds = publiceEdges.map(({ node }) => node.id) + + client.query({ + query: ALL_AUTHORS_PRIVATE, + fetchPolicy: 'network-only', + variables: { ids: publicIds }, + }) } - const loadMore = () => { + // fetch private data for first page + useEffect(() => { + loadPrivate(data) + }, [!!edges, viewer.id]) + + // load next page + const loadMore = async () => { analytics.trackEvent('load_more', { type: 'all_authors', - location: edges.length, + location: edges?.length || 0, }) - return fetchMore({ + + const { data: newData } = await fetchMore({ variables: { - after: pageInfo.endCursor, + after: pageInfo?.endCursor, }, updateQuery: (previousResult, { fetchMoreResult }) => mergeConnections({ @@ -84,6 +75,27 @@ const Authors = () => { dedupe: true, }), }) + + loadPrivate(newData) + } + + /** + * Render + */ + if (loading) { + return + } + + if (error) { + return + } + + if (!edges || edges.length <= 0 || !pageInfo) { + return ( + } + /> + ) } return ( From c8a92905b396ba4cf7bd9afa3c98258226105cb2 Mon Sep 17 00:00:00 2001 From: RoberTu Date: Wed, 15 Jul 2020 21:01:22 +0800 Subject: [PATCH 48/86] feat(public-private-queries): move `` to `components` and break query into public/privates --- src/components/Root/gql.ts | 51 ++++++++++++++ src/components/Root/index.tsx | 120 ++++++++++++++++++++++++++++++++ src/pages/_app.tsx | 124 +--------------------------------- 3 files changed, 174 insertions(+), 121 deletions(-) create mode 100644 src/components/Root/gql.ts create mode 100644 src/components/Root/index.tsx diff --git a/src/components/Root/gql.ts b/src/components/Root/gql.ts new file mode 100644 index 0000000000..9ef76582af --- /dev/null +++ b/src/components/Root/gql.ts @@ -0,0 +1,51 @@ +import gql from 'graphql-tag' + +import { + AnalyticsListener, + FeaturesProvider, + ViewerProvider, +} from '~/components' + +const fragments = { + user: gql` + fragment Viewer on User { + id + ...ViewerUser + ...AnalyticsUser + } + ${ViewerProvider.fragments.user} + ${AnalyticsListener.fragments.user} + `, + official: gql` + fragment Official on Official { + ...FeatureOfficial + } + ${FeaturesProvider.fragments.official} + `, +} + +export const ROOT_QUERY_PUBLIC = gql` + query RootQueryPublic { + viewer { + ...Viewer + } + official { + ...Official + } + } + ${fragments.user} + ${fragments.official} +` + +export const ROOT_QUERY_PRIVATE = gql` + query RootQueryPrivate { + viewer { + ...Viewer + } + official { + ...Official + } + } + ${fragments.user} + ${fragments.official} +` diff --git a/src/components/Root/index.tsx b/src/components/Root/index.tsx new file mode 100644 index 0000000000..216b528516 --- /dev/null +++ b/src/components/Root/index.tsx @@ -0,0 +1,120 @@ +import { useQuery } from '@apollo/react-hooks' +import { InMemoryCache } from 'apollo-cache-inmemory' +import { ApolloClient } from 'apollo-client' +import dynamic from 'next/dynamic' +import { useRouter } from 'next/router' +import React, { useEffect } from 'react' + +import { + AnalyticsListener, + Error, + FeaturesProvider, + LanguageProvider, + Layout, + Toast, + ViewerProvider, +} from '~/components' +import { QueryError } from '~/components/GQL' + +import { PATHS } from '~/common/enums' +import { analytics } from '~/common/utils' + +import { ROOT_QUERY_PRIVATE, ROOT_QUERY_PUBLIC } from './gql' + +import { RootQueryPublic } from './__generated__/RootQueryPublic' + +const DynamicPushInitializer = dynamic( + () => import('~/components/PushInitializer'), + { + ssr: false, + } +) +const DynamicProgressBar = dynamic(() => import('~/components/ProgressBar'), { + ssr: false, +}) +const DynamicGlobalDialogs = dynamic( + () => import('~/components/GlobalDialogs'), + { + ssr: false, + } +) +const DynamicFingerprint = dynamic(() => import('~/components/Fingerprint'), { + ssr: false, +}) + +/** + * `` contains components that depend on viewer + * + */ +// Sentry +import('@sentry/browser').then((Sentry) => { + Sentry.init({ dsn: process.env.NEXT_PUBLIC_SENTRY_DSN || '' }) +}) + +const Root = ({ + client, + children, +}: { + client: ApolloClient + children: React.ReactNode +}) => { + useEffect(() => { + analytics.trackPage() + }) + + useEffect(() => { + analytics.identifyUser() + }, []) + + const router = useRouter() + const isInAbout = router.pathname === PATHS.ABOUT + const isInMigration = router.pathname === PATHS.MIGRATION + const shouldApplyLayout = !isInAbout && !isInMigration + + // anonymous + const { loading, data, error } = useQuery(ROOT_QUERY_PUBLIC) + const viewer = data?.viewer + const official = data?.official + + // viewer + useEffect(() => { + if (!data) { + return + } + client.query({ + query: ROOT_QUERY_PRIVATE, + fetchPolicy: 'network-only', + }) + }, [!!data]) + + if (loading) { + return null + } + + if (error) { + return + } + + if (!viewer) { + return + } + + return ( + + + + {shouldApplyLayout ? {children} : children} + + + + + + + + + + + ) +} + +export default Root diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 1c75713aef..1dcc86d918 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -1,135 +1,17 @@ -import { ApolloProvider, useQuery } from '@apollo/react-hooks' +import { ApolloProvider } from '@apollo/react-hooks' import { getDataFromTree } from '@apollo/react-ssr' import { InMemoryCache } from 'apollo-cache-inmemory' import { ApolloClient } from 'apollo-client' -import gql from 'graphql-tag' import { AppProps } from 'next/app' -import dynamic from 'next/dynamic' -import { useRouter } from 'next/router' -import React, { useEffect } from 'react' -import { - AnalyticsListener, - Error, - ErrorBoundary, - FeaturesProvider, - LanguageProvider, - Layout, - Toast, - ViewerProvider, -} from '~/components' +import { ErrorBoundary } from '~/components' import { ClientUpdater } from '~/components/ClientUpdater' import { GlobalStyles } from '~/components/GlobalStyles' -import { QueryError } from '~/components/GQL' +import Root from '~/components/Root' import SplashScreen from '~/components/SplashScreen' -import { PATHS } from '~/common/enums' -import { analytics } from '~/common/utils' import withApollo from '~/common/utils/withApollo' -import { RootQuery } from './__generated__/RootQuery' - -const ROOT_QUERY = gql` - query RootQuery { - viewer { - id - ...ViewerUser - ...AnalyticsUser - } - official { - ...FeatureOfficial - } - } - ${ViewerProvider.fragments.user} - ${AnalyticsListener.fragments.user} - ${FeaturesProvider.fragments.official} -` - -// Sentry -import('@sentry/browser').then((Sentry) => { - /** - * Initialize - */ - Sentry.init({ dsn: process.env.NEXT_PUBLIC_SENTRY_DSN || '' }) -}) - -/** - * `` contains components that depend on viewer - * - */ -const DynamicPushInitializer = dynamic( - () => import('~/components/PushInitializer'), - { - ssr: false, - } -) -const DynamicProgressBar = dynamic(() => import('~/components/ProgressBar'), { - ssr: false, -}) -const DynamicGlobalDialogs = dynamic( - () => import('~/components/GlobalDialogs'), - { - ssr: false, - } -) -const DynamicFingerprint = dynamic(() => import('~/components/Fingerprint'), { - ssr: false, -}) - -const Root = ({ - client, - children, -}: { - client: ApolloClient - children: React.ReactNode -}) => { - useEffect(() => { - analytics.trackPage() - }) - - useEffect(() => { - analytics.identifyUser() - }, []) - - const router = useRouter() - const isInAbout = router.pathname === PATHS.ABOUT - const isInMigration = router.pathname === PATHS.MIGRATION - const shouldApplyLayout = !isInAbout && !isInMigration - - const { loading, data, error } = useQuery(ROOT_QUERY) - const viewer = data?.viewer - const official = data?.official - - if (loading) { - return null - } - - if (error) { - return - } - - if (!viewer) { - return - } - - return ( - - - - {shouldApplyLayout ? {children} : children} - - - - - - - - - - - ) -} - const MattersApp = ({ Component, pageProps, From 968cc5824d6a1c0f2e19b46393036abefd2e685f Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 15 Jul 2020 23:59:46 +0000 Subject: [PATCH 49/86] build(deps-dev): bump @testing-library/react from 10.4.6 to 10.4.7 Bumps [@testing-library/react](https://github.com/testing-library/react-testing-library) from 10.4.6 to 10.4.7. - [Release notes](https://github.com/testing-library/react-testing-library/releases) - [Changelog](https://github.com/testing-library/react-testing-library/blob/master/CHANGELOG.md) - [Commits](https://github.com/testing-library/react-testing-library/compare/v10.4.6...v10.4.7) Signed-off-by: dependabot-preview[bot] --- package-lock.json | 36 ++++++++++++++++++------------------ package.json | 2 +- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/package-lock.json b/package-lock.json index ff313273db..75da53dfdb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5759,9 +5759,9 @@ "dev": true }, "@testing-library/dom": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-7.20.2.tgz", - "integrity": "sha512-9KOD0fFCTVFsT1EgB8C5qKs1nV7KdIGe0YIANAKeIDWWC0vwkiLXA/8HlrM2+w7YXiRXIeeHh0LxTYQpvaoGgA==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-7.21.0.tgz", + "integrity": "sha512-S8TTMCd7qCkLs6bhdh2+fk7/pIrD16UKvnwa6gJICalZzV1xoAxDY9Isp6qmelizYH4P1Tz+O5Y4nMmjx3x0uQ==", "dev": true, "requires": { "@babel/runtime": "^7.10.3", @@ -5772,9 +5772,9 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.10.4.tgz", - "integrity": "sha512-UpTN5yUJr9b4EX2CnGNWIvER7Ab83ibv0pcvvHc4UOdrBI5jb8bj+32cCwPX6xu0mt2daFNjYhoi+X7beH0RSw==", + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.10.5.tgz", + "integrity": "sha512-otddXKhdNn7d0ptoFRHtMLa8LqDxLYwTjB4nYgM1yy5N6gU/MUf8zqyyLltCH3yAVitBzmwK4us+DD0l/MauAg==", "dev": true, "requires": { "regenerator-runtime": "^0.13.4" @@ -5783,9 +5783,9 @@ } }, "@testing-library/react": { - "version": "10.4.6", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-10.4.6.tgz", - "integrity": "sha512-pVcm8v0HxCEzrtasbC2bdhWM0P5X9o8a/xfBugC97uVVTmhVeGHj+5CdE1JYi/i2K+mCyeq5YqzHLW/gB+K12w==", + "version": "10.4.7", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-10.4.7.tgz", + "integrity": "sha512-hUYbum3X2f1ZKusKfPaooKNYqE/GtPiQ+D2HJaJ4pkxeNJQFVUEvAvEh9+3QuLdBeTWkDMNY5NSijc5+pGdM4Q==", "dev": true, "requires": { "@babel/runtime": "^7.10.3", @@ -5793,9 +5793,9 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.10.4.tgz", - "integrity": "sha512-UpTN5yUJr9b4EX2CnGNWIvER7Ab83ibv0pcvvHc4UOdrBI5jb8bj+32cCwPX6xu0mt2daFNjYhoi+X7beH0RSw==", + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.10.5.tgz", + "integrity": "sha512-otddXKhdNn7d0ptoFRHtMLa8LqDxLYwTjB4nYgM1yy5N6gU/MUf8zqyyLltCH3yAVitBzmwK4us+DD0l/MauAg==", "dev": true, "requires": { "regenerator-runtime": "^0.13.4" @@ -7251,18 +7251,18 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.10.4.tgz", - "integrity": "sha512-UpTN5yUJr9b4EX2CnGNWIvER7Ab83ibv0pcvvHc4UOdrBI5jb8bj+32cCwPX6xu0mt2daFNjYhoi+X7beH0RSw==", + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.10.5.tgz", + "integrity": "sha512-otddXKhdNn7d0ptoFRHtMLa8LqDxLYwTjB4nYgM1yy5N6gU/MUf8zqyyLltCH3yAVitBzmwK4us+DD0l/MauAg==", "dev": true, "requires": { "regenerator-runtime": "^0.13.4" } }, "@babel/runtime-corejs3": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.10.4.tgz", - "integrity": "sha512-BFlgP2SoLO9HJX9WBwN67gHWMBhDX/eDz64Jajd6mR/UAUzqrNMm99d4qHnVaKscAElZoFiPv+JpR/Siud5lXw==", + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.10.5.tgz", + "integrity": "sha512-RMafpmrNB5E/bwdSphLr8a8++9TosnyJp98RZzI6VOx2R2CCMpsXXXRvmI700O9oEKpXdZat6oEK68/F0zjd4A==", "dev": true, "requires": { "core-js-pure": "^3.0.0", diff --git a/package.json b/package.json index 122aeb7d75..6dfedc5883 100644 --- a/package.json +++ b/package.json @@ -89,7 +89,7 @@ "devDependencies": { "@babel/plugin-proposal-optional-chaining": "^7.10.4", "@svgr/webpack": "^5.4.0", - "@testing-library/react": "^10.4.6", + "@testing-library/react": "^10.4.7", "@types/autosize": "^3.0.7", "@types/classnames": "^2.2.10", "@types/express": "^4.17.7", From b4aa055b3b3faca527e394e709763d6f65bbd2dd Mon Sep 17 00:00:00 2001 From: RoberTu Date: Thu, 16 Jul 2020 16:30:25 +0800 Subject: [PATCH 50/86] fix(public-private-queries): fix wrong mergeConnection with @connection `previousResult` of `updateQuery` is undefined due to anonymous viewer and logged-in viewer has different cache key, which causes `mergeConnection` works not as we expect --- src/views/Authors/gql.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/Authors/gql.ts b/src/views/Authors/gql.ts index 29e9937849..90ad81a3d8 100644 --- a/src/views/Authors/gql.ts +++ b/src/views/Authors/gql.ts @@ -4,7 +4,7 @@ import { UserDigest } from '~/components' export const ALL_AUTHORS_PUBLIC = gql` query AllAuthorsPublic($after: String) { - viewer { + viewer @connection(key: "viewerAuthors") { id recommendation { authors(input: { first: 20, after: $after }) { From 85641ba80f7a4c66338916312b7ed39b6725f364 Mon Sep 17 00:00:00 2001 From: RoberTu Date: Thu, 16 Jul 2020 16:46:50 +0800 Subject: [PATCH 51/86] fix(public-private-queries): fix wrong mergeConnection with @connection same reason as commit#b4aa055b3b3faca527e394e709763d6f65bbd2dd --- src/views/Home/Feed/gql.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/views/Home/Feed/gql.ts b/src/views/Home/Feed/gql.ts index e3a219cade..b635107f8a 100644 --- a/src/views/Home/Feed/gql.ts +++ b/src/views/Home/Feed/gql.ts @@ -24,7 +24,7 @@ const feedFragment = gql` export const FEED_ARTICLES_PUBLIC = { hottest: gql` query HottestFeedPublic($after: String) { - viewer { + viewer @connection(key: "viewerHottest") { id recommendation { feed: hottest(input: { first: 10, after: $after }) { @@ -37,7 +37,7 @@ export const FEED_ARTICLES_PUBLIC = { `, valued: gql` query ValuedFeedPublic($after: String) { - viewer { + viewer @connection(key: "viewerValued") { id recommendation { feed: valued(input: { first: 10, after: $after }) { @@ -50,7 +50,7 @@ export const FEED_ARTICLES_PUBLIC = { `, newest: gql` query NewestFeedPublic($after: String) { - viewer { + viewer @connection(key: "viewerNewest") { id recommendation { feed: newest(input: { first: 10, after: $after }) { @@ -63,7 +63,7 @@ export const FEED_ARTICLES_PUBLIC = { `, icymi: gql` query IcymiFeedPublic($after: String) { - viewer { + viewer @connection(key: "viewerIcymi") { id recommendation { feed: icymi(input: { first: 10, after: $after }) { @@ -76,7 +76,7 @@ export const FEED_ARTICLES_PUBLIC = { `, topics: gql` query TopicsFeedPublic($after: String) { - viewer { + viewer @connection(key: "viewerTopics") { id recommendation { feed: topics(input: { first: 10, after: $after }) { From 90b428a0634ccbfb04d57f4ffed3d8cc59c43f9b Mon Sep 17 00:00:00 2001 From: RoberTu Date: Thu, 16 Jul 2020 19:04:58 +0800 Subject: [PATCH 52/86] feat(public-private-queries): `` --- .../SetTagUnselectedButton.tsx | 14 +- src/components/GQL/queries/tagArticles.ts | 16 +- src/views/TagDetail/Articles/Latest/index.tsx | 129 ------------ .../TagDetail/Articles/Selected/index.tsx | 128 ------------ src/views/TagDetail/Articles/index.tsx | 184 +++++++++++++++++- src/views/TagDetail/index.tsx | 90 ++++----- 6 files changed, 244 insertions(+), 317 deletions(-) delete mode 100644 src/views/TagDetail/Articles/Latest/index.tsx delete mode 100644 src/views/TagDetail/Articles/Selected/index.tsx diff --git a/src/components/ArticleDigest/DropdownActions/SetTagUnselectedButton.tsx b/src/components/ArticleDigest/DropdownActions/SetTagUnselectedButton.tsx index e29534ac02..2d1a5e5766 100644 --- a/src/components/ArticleDigest/DropdownActions/SetTagUnselectedButton.tsx +++ b/src/components/ArticleDigest/DropdownActions/SetTagUnselectedButton.tsx @@ -10,9 +10,9 @@ import { ADD_TOAST } from '~/common/enums' import { getQuery } from '~/common/utils' import { - TagArticles, - TagArticles_node_Tag, -} from '~/components/GQL/queries/__generated__/TagArticles' + TagArticlesPublic, + TagArticlesPublic_node_Tag, +} from '~/components/GQL/queries/__generated__/TagArticlesPublic' import { SetTagUnselected } from './__generated__/SetTagUnselected' import { SetTagUnselectedButtonArticle } from './__generated__/SetTagUnselectedButtonArticle' @@ -57,10 +57,12 @@ const SetTagUnselectedButton = ({ variables: { id: tagId, articles: [article.id] }, update: (cache) => { try { - const query = require('~/components/GQL/queries/tagArticles').default + const { + TAG_ARTICLES_PUBLIC: query, + } = require('~/components/GQL/queries/tagArticles').default const variables = { id: tagId, selected: true } - const data = cache.readQuery({ query, variables }) - const node = _get(data, 'node', {}) as TagArticles_node_Tag + const data = cache.readQuery({ query, variables }) + const node = _get(data, 'node', {}) as TagArticlesPublic_node_Tag if ( !node.articles || !node.articles.edges || diff --git a/src/components/GQL/queries/tagArticles.ts b/src/components/GQL/queries/tagArticles.ts index a4a124c870..a89f016518 100644 --- a/src/components/GQL/queries/tagArticles.ts +++ b/src/components/GQL/queries/tagArticles.ts @@ -2,8 +2,8 @@ import gql from 'graphql-tag' import { ArticleDigestFeed } from '~/components' -export default gql` - query TagArticles($id: ID!, $after: String, $selected: Boolean) { +export const TAG_ARTICLES_PUBLIC = gql` + query TagArticlesPublic($id: ID!, $after: String, $selected: Boolean) { node(input: { id: $id }) { ... on Tag { id @@ -27,3 +27,15 @@ export default gql` ${ArticleDigestFeed.fragments.article.public} ${ArticleDigestFeed.fragments.article.private} ` + +export const TAG_ARTICLES_PRIVATE = gql` + query TagArticlesPrivate($ids: [ID!]!) { + nodes(input: { ids: $ids }) { + id + ... on Article { + ...ArticleDigestFeedArticlePrivate + } + } + } + ${ArticleDigestFeed.fragments.article.private} +` diff --git a/src/views/TagDetail/Articles/Latest/index.tsx b/src/views/TagDetail/Articles/Latest/index.tsx deleted file mode 100644 index c3aaf2f89d..0000000000 --- a/src/views/TagDetail/Articles/Latest/index.tsx +++ /dev/null @@ -1,129 +0,0 @@ -import { useQuery } from '@apollo/react-hooks' -import { NetworkStatus } from 'apollo-client' -import _get from 'lodash/get' - -import { - ArticleDigestFeed, - EmptyTagArticles, - InfiniteScroll, - List, - Spinner, - useEventListener, - usePullToRefresh, -} from '~/components' -import { QueryError } from '~/components/GQL' -import TAG_ARTICLES from '~/components/GQL/queries/tagArticles' - -import { REFETCH_TAG_DETAIL_ARTICLES } from '~/common/enums' -import { analytics, mergeConnections } from '~/common/utils' - -import { - TagArticles, - TagArticles_node_Tag_articles, -} from '~/components/GQL/queries/__generated__/TagArticles' - -const LatestArticles = ({ id }: { id: string }) => { - const { data, loading, error, fetchMore, refetch, networkStatus } = useQuery< - TagArticles - >(TAG_ARTICLES, { - variables: { id }, - fetchPolicy: 'cache-and-network', - notifyOnNetworkStatusChange: true, - }) - - const connectionPath = 'node.articles' - const articles = _get(data, connectionPath) as TagArticles_node_Tag_articles - const { edges, pageInfo } = articles || { edges: [], pageInfo: {} } - const isNewLoading = networkStatus === NetworkStatus.loading - const hasArticles = edges && edges.length > 0 && pageInfo - - const loadMore = () => { - analytics.trackEvent('load_more', { - type: 'tag_detail_selected', - location: edges ? edges.length : 0, - }) - return fetchMore({ - variables: { - after: pageInfo.endCursor, - }, - updateQuery: (previousResult, { fetchMoreResult }) => - mergeConnections({ - oldData: previousResult, - newData: fetchMoreResult, - path: connectionPath, - }), - }) - } - - const sync = ({ - event, - differences = 0, - }: { - event: 'add' | 'delete' - differences?: number - }) => { - const { edges: items } = _get(data, connectionPath, { edges: [] }) - switch (event) { - case 'add': - refetch({ - variables: { - id, - first: items.length + differences, - }, - }) - break - case 'delete': - refetch({ - variables: { - id, - first: Math.max(items.length - 1, 0), - }, - }) - break - } - } - - useEventListener(REFETCH_TAG_DETAIL_ARTICLES, sync) - usePullToRefresh.Handler(refetch) - - if (loading && (!articles || isNewLoading)) { - return - } - - if (error) { - return - } - - if (!data || !data.node || data.node.__typename !== 'Tag') { - return - } - - if (!hasArticles) { - return - } - - return ( - - - {(edges || []).map(({ node, cursor }, i) => ( - - - analytics.trackEvent('click_feed', { - type: 'tag_detail_latest', - contentType: 'article', - styleType: 'title', - location: i, - }) - } - inTagDetailLatest - /> - - ))} - - - ) -} - -export default LatestArticles diff --git a/src/views/TagDetail/Articles/Selected/index.tsx b/src/views/TagDetail/Articles/Selected/index.tsx deleted file mode 100644 index 8c5214074b..0000000000 --- a/src/views/TagDetail/Articles/Selected/index.tsx +++ /dev/null @@ -1,128 +0,0 @@ -import { useQuery } from '@apollo/react-hooks' -import { NetworkStatus } from 'apollo-client' -import _get from 'lodash/get' - -import { - ArticleDigestFeed, - EmptyTagArticles, - InfiniteScroll, - List, - Spinner, - useEventListener, - usePullToRefresh, -} from '~/components' -import { QueryError } from '~/components/GQL' -import TAG_ARTICLES from '~/components/GQL/queries/tagArticles' - -import { REFETCH_TAG_DETAIL_ARTICLES } from '~/common/enums' -import { analytics, mergeConnections } from '~/common/utils' - -import { - TagArticles, - TagArticles_node_Tag_articles, -} from '~/components/GQL/queries/__generated__/TagArticles' - -const SelectedArticles = ({ id }: { id: string }) => { - const { data, loading, error, fetchMore, refetch, networkStatus } = useQuery< - TagArticles - >(TAG_ARTICLES, { - variables: { id, selected: true }, - fetchPolicy: 'cache-and-network', - notifyOnNetworkStatusChange: true, - }) - - const connectionPath = 'node.articles' - const articles = _get(data, connectionPath) as TagArticles_node_Tag_articles - const { edges, pageInfo } = articles || { edges: [], pageInfo: {} } - const isNewLoading = networkStatus === NetworkStatus.loading - const hasArticles = edges && edges.length > 0 && pageInfo - - const loadMore = () => { - analytics.trackEvent('load_more', { - type: 'tag_detail_selected', - location: edges ? edges.length : 0, - }) - return fetchMore({ - variables: { - after: pageInfo.endCursor, - }, - updateQuery: (previousResult, { fetchMoreResult }) => - mergeConnections({ - oldData: previousResult, - newData: fetchMoreResult, - path: connectionPath, - }), - }) - } - const sync = ({ - event, - differences = 0, - }: { - event: 'add' | 'delete' - differences?: number - }) => { - const { edges: items } = _get(data, 'node.articles', { edges: [] }) - switch (event) { - case 'add': - refetch({ - variables: { - id, - first: items.length + differences, - }, - }) - break - case 'delete': - refetch({ - variables: { - id, - first: Math.max(items.length - 1, 0), - }, - }) - break - } - } - - useEventListener(REFETCH_TAG_DETAIL_ARTICLES, sync) - usePullToRefresh.Handler(refetch) - - if (loading && (!articles || isNewLoading)) { - return - } - - if (error) { - return - } - - if (!data || !data.node || data.node.__typename !== 'Tag') { - return - } - - if (!hasArticles) { - return - } - - return ( - - - {(edges || []).map(({ node, cursor }, i) => ( - - - analytics.trackEvent('click_feed', { - type: 'tag_detail_selected', - styleType: 'title', - contentType: 'article', - location: i, - }) - } - inTagDetailSelected - /> - - ))} - - - ) -} - -export default SelectedArticles diff --git a/src/views/TagDetail/Articles/index.tsx b/src/views/TagDetail/Articles/index.tsx index 1a13bcd48e..9c0824f2ff 100644 --- a/src/views/TagDetail/Articles/index.tsx +++ b/src/views/TagDetail/Articles/index.tsx @@ -1,7 +1,181 @@ -import Latest from './Latest' -import Selected from './Selected' +import { useQuery } from '@apollo/react-hooks' +import { NetworkStatus } from 'apollo-client' +import { useContext, useEffect } from 'react' -export const TagDetailArticles = { - Latest, - Selected, +import { + ArticleDigestFeed, + EmptyTagArticles, + InfiniteScroll, + List, + Spinner, + useEventListener, + usePullToRefresh, + ViewerContext, +} from '~/components' +import { QueryError } from '~/components/GQL' +import { + TAG_ARTICLES_PRIVATE, + TAG_ARTICLES_PUBLIC, +} from '~/components/GQL/queries/tagArticles' + +import { REFETCH_TAG_DETAIL_ARTICLES } from '~/common/enums' +import { analytics, mergeConnections } from '~/common/utils' + +import { TagArticlesPublic } from '~/components/GQL/queries/__generated__/TagArticlesPublic' + +interface TagArticlesProps { + tagId: string + selected?: boolean +} + +const TagDetailArticles = ({ tagId, selected }: TagArticlesProps) => { + const viewer = useContext(ViewerContext) + + /** + * Data Fetching + */ + // public data + const { + data, + loading, + error, + fetchMore, + refetch, + networkStatus, + client, + } = useQuery(TAG_ARTICLES_PUBLIC, { + variables: { id: tagId, selected }, + notifyOnNetworkStatusChange: true, + }) + + // pagination + const connectionPath = 'node.articles' + const { edges, pageInfo } = + (data?.node?.__typename === 'Tag' && + data.node.articles && + data.node.articles) || + {} + const isNewLoading = networkStatus === NetworkStatus.loading + + // private data + const loadPrivate = (publicData?: TagArticlesPublic) => { + if (!viewer.id || !publicData) { + return + } + + const publiceEdges = + publicData?.node?.__typename === 'Tag' + ? publicData.node.articles.edges || [] + : [] + const publicIds = publiceEdges.map(({ node }) => node.id) + + client.query({ + query: TAG_ARTICLES_PRIVATE, + fetchPolicy: 'network-only', + variables: { ids: publicIds }, + }) + } + + // fetch private data for first page + useEffect(() => { + if (loading || !edges) { + return + } + + loadPrivate(data) + }, [!!edges, loading, selected, viewer.id]) + + // load next page + const loadMore = async () => { + analytics.trackEvent('load_more', { + type: selected ? 'tag_detail_selected' : 'tag_detail_latest', + location: edges ? edges.length : 0, + }) + + const { data: newData } = await fetchMore({ + variables: { + after: pageInfo?.endCursor, + }, + updateQuery: (previousResult, { fetchMoreResult }) => + mergeConnections({ + oldData: previousResult, + newData: fetchMoreResult, + path: connectionPath, + }), + }) + + loadPrivate(newData) + } + + // refetch, sync & pull to refresh + const sync = ({ + event, + differences = 0, + }: { + event: 'add' | 'delete' + differences?: number + }) => { + const count = (edges || []).length + switch (event) { + case 'add': + refetch({ + variables: { + id: tagId, + first: count + differences, + }, + }) + break + case 'delete': + refetch({ + variables: { + id: tagId, + first: Math.max(count - 1, 0), + }, + }) + break + } + } + + useEventListener(REFETCH_TAG_DETAIL_ARTICLES, sync) + usePullToRefresh.Handler(refetch) + + /** + * Render + */ + if (loading && isNewLoading) { + return + } + + if (error) { + return + } + + if (!edges || edges.length <= 0 || !pageInfo) { + return + } + + return ( + + + {(edges || []).map(({ node, cursor }, i) => ( + + + analytics.trackEvent('click_feed', { + type: selected ? 'tag_detail_selected' : 'tag_detail_latest', + contentType: 'article', + styleType: 'title', + location: i, + }) + } + inTagDetailLatest + /> + + ))} + + + ) } + +export default TagDetailArticles diff --git a/src/views/TagDetail/index.tsx b/src/views/TagDetail/index.tsx index 965e0e3a28..ccbaaed814 100644 --- a/src/views/TagDetail/index.tsx +++ b/src/views/TagDetail/index.tsx @@ -1,10 +1,9 @@ import { useQuery } from '@apollo/react-hooks' import gql from 'graphql-tag' import _find from 'lodash/find' -import _get from 'lodash/get' import _some from 'lodash/some' import { useRouter } from 'next/router' -import { useContext, useState } from 'react' +import { useContext, useEffect, useState } from 'react' import { EmptyTag, @@ -26,14 +25,18 @@ import { UserDigest } from '~/components/UserDigest' import { ERROR_CODES } from '~/common/enums' import { getQuery } from '~/common/utils' -import { TagDetailArticles } from './Articles' +import TagDetailArticles from './Articles' import ArticlesCount from './ArticlesCount' import { TagDetailButtons } from './Buttons' import DropdownActions from './DropdownActions' import Followers from './Followers' import styles from './styles.css' -import { TagDetail as TagDetailType } from './__generated__/TagDetail' +import { + TagDetail as TagDetailType, + TagDetail_node_Tag, + TagDetail_node_Tag_editors, +} from './__generated__/TagDetail' const TAG_DETAIL = gql` query TagDetail($id: ID!) { @@ -61,7 +64,7 @@ const TAG_DETAIL = gql` ${TagDetailButtons.FollowButton.fragments.tag} ` -type TagFeed = 'latest' | 'selected' +type TagFeedType = 'latest' | 'selected' const EmptyLayout: React.FC = ({ children }) => ( @@ -70,33 +73,39 @@ const EmptyLayout: React.FC = ({ children }) => ( ) -const TagDetail = ({ data }: { data: TagDetailType }) => { +const TagDetail = ({ tag }: { tag: TagDetail_node_Tag }) => { const viewer = useContext(ViewerContext) - const hasSelected = _get(data, 'node.articles.totalCount', 0) - const [feed, setFeed] = useState(hasSelected ? 'selected' : 'latest') - if (!data || !data.node || data.node.__typename !== 'Tag') { - return - } + // feed type + const hasSelected = (tag?.articles.totalCount || 0) > 0 + const [feed, setFeed] = useState( + hasSelected ? 'selected' : 'latest' + ) + const isSelected = feed === 'selected' - if (hasSelected === 0 && feed === 'selected') { - setFeed('latest') - } + useEffect(() => { + if (!hasSelected && isSelected) { + setFeed('latest') + } + }) - const filter = ({ displayName }: any) => + // define permission + const filter = ({ displayName }: TagDetail_node_Tag_editors) => (displayName || '').toLowerCase() !== 'matty' - const editors = data.node.editors || [] + const editors = tag?.editors || [] const owner = _find(editors, filter) - // define permission const normalEditors = editors.filter(filter) const isEditor = _some(editors, (editor) => editor.id === viewer.id) - const isCreator = data.node.creator?.id === viewer.id + const isCreator = tag?.creator?.id === viewer.id const isMaintainer = isEditor || (normalEditors.length === 0 && isCreator) || viewer.info.email === 'hi@matters.news' + /** + * Render + */ return ( { } /> - + @@ -135,51 +144,38 @@ const TagDetail = ({ data }: { data: TagDetailType }) => {
)} - #{data.node.content} + #{tag.content} - {data.node.description && ( + {tag.description && ( -

{data.node.description}

+

{tag.description}

)}
- - + +
- - + +
- {hasSelected > 0 && ( - setFeed('selected')} - > + {hasSelected && ( + setFeed('selected')}> )} - setFeed('latest')} - > + setFeed('latest')}> - {feed === 'selected' ? ( - - ) : ( - - )} +
@@ -228,7 +224,7 @@ const TagDetailContainer = () => { ) } - return + return } export default TagDetailContainer From 51a50ce02a4241b869b35213de118f2a0d831009 Mon Sep 17 00:00:00 2001 From: RoberTu Date: Thu, 16 Jul 2020 19:32:56 +0800 Subject: [PATCH 53/86] feat(public-private-queries): skip SSR for protected routes --- src/components/Context/Viewer/index.tsx | 11 +++++++++-- src/components/Protected/index.tsx | 12 ++---------- src/components/Root/index.tsx | 17 +++++++++++------ 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/components/Context/Viewer/index.tsx b/src/components/Context/Viewer/index.tsx index 06bafbee96..989f194ae9 100644 --- a/src/components/Context/Viewer/index.tsx +++ b/src/components/Context/Viewer/index.tsx @@ -46,9 +46,13 @@ export type Viewer = ViewerUser & { isInactive: boolean isCivicLiker: boolean shouldSetupLikerID: boolean + privateFetched: boolean } -export const processViewer = (viewer: ViewerUser): Viewer => { +export const processViewer = ( + viewer: ViewerUser, + privateFetched: boolean +): Viewer => { const isAuthed = !!viewer.id const state = viewer?.status?.state const isActive = state === 'active' @@ -80,6 +84,7 @@ export const processViewer = (viewer: ViewerUser): Viewer => { isInactive, isCivicLiker, shouldSetupLikerID, + privateFetched, } } @@ -90,12 +95,14 @@ export const ViewerConsumer = ViewerContext.Consumer export const ViewerProvider = ({ children, viewer, + privateFetched, }: { children: React.ReactNode viewer: ViewerUser + privateFetched: boolean }) => { return ( - + {children} ) diff --git a/src/components/Protected/index.tsx b/src/components/Protected/index.tsx index 6160424a15..85e4d308b6 100644 --- a/src/components/Protected/index.tsx +++ b/src/components/Protected/index.tsx @@ -8,23 +8,15 @@ export const Protected: React.FC = ({ children }) => { const viewer = useContext(ViewerContext) useEffect(() => { - if (!viewer.isAuthed && process.browser) { + if (viewer.privateFetched && !viewer.isAuthed) { redirectToLogin() } - }, []) + }, [viewer.privateFetched]) if (viewer.isAuthed) { return <>{children} } - if (!process.browser) { - return ( - - - - ) - } - return ( diff --git a/src/components/Root/index.tsx b/src/components/Root/index.tsx index 216b528516..abaaf4b863 100644 --- a/src/components/Root/index.tsx +++ b/src/components/Root/index.tsx @@ -3,7 +3,7 @@ import { InMemoryCache } from 'apollo-cache-inmemory' import { ApolloClient } from 'apollo-client' import dynamic from 'next/dynamic' import { useRouter } from 'next/router' -import React, { useEffect } from 'react' +import React, { useEffect, useState } from 'react' import { AnalyticsListener, @@ -77,14 +77,19 @@ const Root = ({ const official = data?.official // viewer + const [privateFetched, setPrivateFetched] = useState(false) + const fetchPrivateViewer = async () => { + await client.query({ + query: ROOT_QUERY_PRIVATE, + fetchPolicy: 'network-only', + }) + setPrivateFetched(true) + } useEffect(() => { if (!data) { return } - client.query({ - query: ROOT_QUERY_PRIVATE, - fetchPolicy: 'network-only', - }) + fetchPrivateViewer() }, [!!data]) if (loading) { @@ -100,7 +105,7 @@ const Root = ({ } return ( - + {shouldApplyLayout ? {children} : children} From 000a137d3b7289cefe782e42b3780a80c9bc8615 Mon Sep 17 00:00:00 2001 From: RoberTu Date: Thu, 16 Jul 2020 19:47:06 +0800 Subject: [PATCH 54/86] feat(public-private-queries): group routes --- src/common/enums/route.ts | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/common/enums/route.ts b/src/common/enums/route.ts index bdd405df06..c7f2258c3a 100644 --- a/src/common/enums/route.ts +++ b/src/common/enums/route.ts @@ -56,10 +56,14 @@ export const ROUTES: Array<{ pathname: string handler?: (req: Request, res: Response, next: NextFunction) => any }> = [ + /** + * Public + */ { key: 'HOME', pathname: '/' }, { key: 'FOLLOW', pathname: '/follow' }, { key: 'AUTHORS', pathname: '/authors' }, { key: 'SEARCH', pathname: '/search' }, + // experient page for recommendation engine testing { key: 'RECOMMENDATION', pathname: '/recommendation' }, @@ -76,6 +80,22 @@ export const ROUTES: Array<{ // Article { key: 'ARTICLE_DETAIL', pathname: '/[userName]/[mediaHash]' }, + // Auth + { key: 'LOGIN', pathname: '/login' }, + { key: 'SIGNUP', pathname: '/signup' }, + { key: 'FORGET', pathname: '/forget' }, + + // Misc + { key: 'HELP', pathname: '/help' }, + { key: 'MIGRATION', pathname: '/migration' }, + { key: 'ABOUT', pathname: '/about' }, + { key: 'GUIDE', pathname: '/guide' }, + { key: 'COMMUNITY', pathname: '/community' }, + { key: 'TOS', pathname: '/tos' }, + + /** + * Protected + */ // Me { key: 'ME_DRAFTS', pathname: '/me/drafts' }, { key: 'ME_BOOKMARKS', pathname: '/me/bookmarks' }, @@ -103,11 +123,6 @@ export const ROUTES: Array<{ // Draft { key: 'ME_DRAFT_DETAIL', pathname: '/me/drafts/[draftId]' }, - // Auth - { key: 'LOGIN', pathname: '/login' }, - { key: 'SIGNUP', pathname: '/signup' }, - { key: 'FORGET', pathname: '/forget' }, - // OAuth { key: 'OAUTH_AUTHORIZE', pathname: '/oauth/authorize' }, { key: 'OAUTH_CALLBACK_SUCCESS', pathname: '/oauth/[provider]/success' }, @@ -116,14 +131,6 @@ export const ROUTES: Array<{ // Pay { key: 'PAY_CALLBACK_SUCCESS', pathname: '/pay/[provider]/success' }, { key: 'PAY_CALLBACK_FAILURE', pathname: '/pay/[provider]/failure' }, - - // Misc - { key: 'HELP', pathname: '/help' }, - { key: 'MIGRATION', pathname: '/migration' }, - { key: 'ABOUT', pathname: '/about' }, - { key: 'GUIDE', pathname: '/guide' }, - { key: 'COMMUNITY', pathname: '/community' }, - { key: 'TOS', pathname: '/tos' }, ] export const UrlFragments = { From d171e3aec76b606935c15832dadf6dbd9850d190 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 16 Jul 2020 21:41:39 +0000 Subject: [PATCH 55/86] build(deps-dev): bump chromedriver from 83.0.1 to 84.0.0 Bumps [chromedriver](https://github.com/giggio/node-chromedriver) from 83.0.1 to 84.0.0. - [Release notes](https://github.com/giggio/node-chromedriver/releases) - [Commits](https://github.com/giggio/node-chromedriver/commits) Signed-off-by: dependabot-preview[bot] --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 75da53dfdb..06c15322c9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8959,9 +8959,9 @@ } }, "chromedriver": { - "version": "83.0.1", - "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-83.0.1.tgz", - "integrity": "sha512-51/YsLIMRF+L0ooMlM4aZjyoOpDs0gDXGlT6+/CwWEnvK53PUyef9FkotKbzknCaUeL/qUw3ic3IMmsNc+SUxg==", + "version": "84.0.0", + "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-84.0.0.tgz", + "integrity": "sha512-fNX9eT1C38D1W8r5ss9ty42eDK+GIkCZVKukfeDs0XSBeKfyT0o/vbMdPr9MUkWQ+vIcFAS5hFGp9E3+xoaMeQ==", "dev": true, "requires": { "@testim/chrome-version": "^1.0.7", diff --git a/package.json b/package.json index 6dfedc5883..b1998e4bdc 100644 --- a/package.json +++ b/package.json @@ -115,7 +115,7 @@ "babel-jest": "^26.1.0", "babel-plugin-dynamic-import-node": "^2.3.3", "babel-polyfill": "^6.26.0", - "chromedriver": "^83.0.1", + "chromedriver": "^84.0.0", "cucumber": "^6.0.5", "cucumber-pretty": "^6.0.0", "cz-conventional-changelog": "^3.2.0", From e0178b30ab100121d5ed75bcd38c594e2936670a Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 17 Jul 2020 03:49:07 +0000 Subject: [PATCH 56/86] build(deps-dev): bump typescript from 3.9.6 to 3.9.7 Bumps [typescript](https://github.com/Microsoft/TypeScript) from 3.9.6 to 3.9.7. - [Release notes](https://github.com/Microsoft/TypeScript/releases) - [Commits](https://github.com/Microsoft/TypeScript/compare/v3.9.6...v3.9.7) Signed-off-by: dependabot-preview[bot] --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 75da53dfdb..96945956f3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25938,9 +25938,9 @@ } }, "typescript": { - "version": "3.9.6", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.6.tgz", - "integrity": "sha512-Pspx3oKAPJtjNwE92YS05HQoY7z2SFyOpHo9MqJor3BXAGNaPUs83CuVp9VISFkSjyRfiTpmKuAYGJB7S7hOxw==", + "version": "3.9.7", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz", + "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==", "dev": true }, "ua-parser-js": { diff --git a/package.json b/package.json index 348277f8e6..17871415ab 100644 --- a/package.json +++ b/package.json @@ -153,7 +153,7 @@ "tslint-config-prettier": "^1.18.0", "tslint-react": "^4.2.0", "tslint-react-hooks": "^2.2.2", - "typescript": "^3.9.6" + "typescript": "^3.9.7" }, "prettier": { "singleQuote": true, From cc9cafa104c4a7e8aac13de74121145ae57aa1e4 Mon Sep 17 00:00:00 2001 From: RoberTu Date: Fri, 17 Jul 2020 12:49:02 +0800 Subject: [PATCH 57/86] fix(public-private-queries): add @connection to FeedAuthorsPublic and SidebarAuthorsPublic queries --- .../GQL/queries/unreadResponseInfoPopUp.ts | 12 ------------ src/views/Home/Feed/Authors/gql.ts | 2 +- src/views/Home/Feed/gql.ts | 10 +++++----- src/views/Home/Sidebar/Authors/gql.ts | 2 +- 4 files changed, 7 insertions(+), 19 deletions(-) delete mode 100644 src/components/GQL/queries/unreadResponseInfoPopUp.ts diff --git a/src/components/GQL/queries/unreadResponseInfoPopUp.ts b/src/components/GQL/queries/unreadResponseInfoPopUp.ts deleted file mode 100644 index 40026c5ade..0000000000 --- a/src/components/GQL/queries/unreadResponseInfoPopUp.ts +++ /dev/null @@ -1,12 +0,0 @@ -import gql from 'graphql-tag' - -export default gql` - query UnreadResponseInfoPopUp { - viewer { - id - status { - unreadResponseInfoPopUp - } - } - } -` diff --git a/src/views/Home/Feed/Authors/gql.ts b/src/views/Home/Feed/Authors/gql.ts index 16d4e9def9..e4a4142147 100644 --- a/src/views/Home/Feed/Authors/gql.ts +++ b/src/views/Home/Feed/Authors/gql.ts @@ -5,7 +5,7 @@ import { UserDigest } from '~/components' export const FEED_AUTHORS_PUBLIC = gql` query FeedAuthorsPublic { - viewer { + viewer @connection(key: "viewerFeedAuthors") { id recommendation { authors( diff --git a/src/views/Home/Feed/gql.ts b/src/views/Home/Feed/gql.ts index b635107f8a..345d0e7a57 100644 --- a/src/views/Home/Feed/gql.ts +++ b/src/views/Home/Feed/gql.ts @@ -24,7 +24,7 @@ const feedFragment = gql` export const FEED_ARTICLES_PUBLIC = { hottest: gql` query HottestFeedPublic($after: String) { - viewer @connection(key: "viewerHottest") { + viewer @connection(key: "viewerFeedHottest") { id recommendation { feed: hottest(input: { first: 10, after: $after }) { @@ -37,7 +37,7 @@ export const FEED_ARTICLES_PUBLIC = { `, valued: gql` query ValuedFeedPublic($after: String) { - viewer @connection(key: "viewerValued") { + viewer @connection(key: "viewerFeedValued") { id recommendation { feed: valued(input: { first: 10, after: $after }) { @@ -50,7 +50,7 @@ export const FEED_ARTICLES_PUBLIC = { `, newest: gql` query NewestFeedPublic($after: String) { - viewer @connection(key: "viewerNewest") { + viewer @connection(key: "viewerFeedNewest") { id recommendation { feed: newest(input: { first: 10, after: $after }) { @@ -63,7 +63,7 @@ export const FEED_ARTICLES_PUBLIC = { `, icymi: gql` query IcymiFeedPublic($after: String) { - viewer @connection(key: "viewerIcymi") { + viewer @connection(key: "viewerFeedIcymi") { id recommendation { feed: icymi(input: { first: 10, after: $after }) { @@ -76,7 +76,7 @@ export const FEED_ARTICLES_PUBLIC = { `, topics: gql` query TopicsFeedPublic($after: String) { - viewer @connection(key: "viewerTopics") { + viewer @connection(key: "viewerFeedTopics") { id recommendation { feed: topics(input: { first: 10, after: $after }) { diff --git a/src/views/Home/Sidebar/Authors/gql.ts b/src/views/Home/Sidebar/Authors/gql.ts index f688b3c766..381746ebf6 100644 --- a/src/views/Home/Sidebar/Authors/gql.ts +++ b/src/views/Home/Sidebar/Authors/gql.ts @@ -4,7 +4,7 @@ import { UserDigest } from '~/components' export const SIDEBAR_AUTHORS_PUBLIC = gql` query SidebarAuthorsPublic { - viewer { + viewer @connection(key: "viewerSidebarAuthors") { id recommendation { authors( From 2f148493de877e9edab88a402caba5fdf2e88963 Mon Sep 17 00:00:00 2001 From: RoberTu Date: Fri, 17 Jul 2020 13:47:53 +0800 Subject: [PATCH 58/86] feat(public-private-queries): log graphql operation name --- src/common/utils/withApollo.ts | 6 +++++- src/views/Home/Feed/Tags/index.tsx | 8 ++++---- src/views/Home/Sidebar/Tags/index.tsx | 8 ++++---- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/common/utils/withApollo.ts b/src/common/utils/withApollo.ts index 9490b725eb..33c6f4d696 100644 --- a/src/common/utils/withApollo.ts +++ b/src/common/utils/withApollo.ts @@ -67,7 +67,11 @@ const errorLink = onError(({ graphQLErrors, networkError }) => { } }) -const authLink = setContext((_, { headers }) => { +const authLink = setContext((operation, { headers }) => { + if (process.env.NODE_ENV !== 'production') { + console.log(`\x1b[32m[GraphQL operation]\x1b[0m`, operation.operationName) + } + return { headers: { ...headers, diff --git a/src/views/Home/Feed/Tags/index.tsx b/src/views/Home/Feed/Tags/index.tsx index 5b9d57551e..978a5b3792 100644 --- a/src/views/Home/Feed/Tags/index.tsx +++ b/src/views/Home/Feed/Tags/index.tsx @@ -9,11 +9,11 @@ import { analytics } from '~/common/utils' import SectionHeader from '../../SectionHeader' import TagFeedDigest from './TagFeedDigest' -import { FeedTags } from './__generated__/FeedTags' +import { FeedTagsPublic } from './__generated__/FeedTagsPublic' const FEED_TAGS = gql` - query FeedTags { - viewer { + query FeedTagsPublic { + viewer @connection(key: "viewerFeedTags") { id recommendation { tags(input: { first: 5 }) { @@ -31,7 +31,7 @@ const FEED_TAGS = gql` ` const TagsFeed = () => { - const { data, loading, error } = useQuery(FEED_TAGS) + const { data, loading, error } = useQuery(FEED_TAGS) const edges = data?.viewer?.recommendation.tags.edges if (error) { diff --git a/src/views/Home/Sidebar/Tags/index.tsx b/src/views/Home/Sidebar/Tags/index.tsx index 2d2d0188c4..624e71a91e 100644 --- a/src/views/Home/Sidebar/Tags/index.tsx +++ b/src/views/Home/Sidebar/Tags/index.tsx @@ -8,11 +8,11 @@ import { analytics, toPath } from '~/common/utils' import SectionHeader from '../../SectionHeader' -import { SidebarTags } from './__generated__/SidebarTags' +import { SidebarTagsPublic } from './__generated__/SidebarTagsPublic' const SIDEBAR_TAGS = gql` - query SidebarTags { - viewer { + query SidebarTagsPublic { + viewer @connection(key: "viewerSidebarTags") { id recommendation { tags(input: { first: 5 }) { @@ -30,7 +30,7 @@ const SIDEBAR_TAGS = gql` ` const Tags = () => { - const { data, loading, error } = useQuery(SIDEBAR_TAGS) + const { data, loading, error } = useQuery(SIDEBAR_TAGS) const edges = data?.viewer?.recommendation.tags.edges if (error) { From 02c8e024e3b2a3e2d5cebb16f882e193dc767778 Mon Sep 17 00:00:00 2001 From: RoberTu Date: Fri, 17 Jul 2020 15:52:44 +0800 Subject: [PATCH 59/86] feat(queries): use `toggle*` mutations; init push on private viewer fetched; --- package.json | 2 +- src/components/BlockUser/Button/index.tsx | 10 ++--- src/components/BlockUser/Dialog/index.tsx | 10 ++--- src/components/Buttons/Bookmark/Subscribe.tsx | 2 +- .../Buttons/Bookmark/Unsubscribe.tsx | 2 +- src/components/Buttons/Follow/Follow.tsx | 18 ++------ src/components/Buttons/Follow/Unfollow.tsx | 18 ++------ src/components/Buttons/UnblockUser/index.tsx | 10 ++--- .../Comment/DropdownActions/PinButton.tsx | 42 ++++--------------- src/components/GQL/mutations/blockUser.ts | 10 ----- .../GQL/mutations/toggleBlockUser.ts | 10 +++++ .../GQL/mutations/toggleFollowTag.ts | 10 +++++ .../GQL/mutations/toggleFollowUser.ts | 11 +++++ .../GQL/mutations/togglePinComment.ts | 14 +++++++ .../GQL/mutations/toggleSubscribeArticle.ts | 4 +- src/components/GQL/mutations/unblockUser.ts | 10 ----- src/components/PushInitializer/index.tsx | 6 ++- src/views/Home/Feed/index.tsx | 2 +- .../TagDetail/Buttons/FollowButton/Follow.tsx | 17 ++------ .../Buttons/FollowButton/Unfollow.tsx | 17 ++------ 20 files changed, 94 insertions(+), 131 deletions(-) delete mode 100644 src/components/GQL/mutations/blockUser.ts create mode 100644 src/components/GQL/mutations/toggleBlockUser.ts create mode 100644 src/components/GQL/mutations/toggleFollowTag.ts create mode 100644 src/components/GQL/mutations/toggleFollowUser.ts create mode 100644 src/components/GQL/mutations/togglePinComment.ts delete mode 100644 src/components/GQL/mutations/unblockUser.ts diff --git a/package.json b/package.json index 348277f8e6..792055aacb 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "analyze": "BUNDLE_ANALYZE=both next build", "analyze:server": "BUNDLE_ANALYZE=server next build", "analyze:browser": "BUNDLE_ANALYZE=browser next build", - "gen:type": "npm run gen:clean && apollo client:codegen __generated__ --target=typescript --includes 'src/**/*.{ts,tsx}' --endpoint 'https://server-stage.matters.news/graphql'", + "gen:type": "npm run gen:clean && apollo client:codegen __generated__ --target=typescript --includes 'src/**/*.{ts,tsx}' --endpoint 'http://matters-server-develop.ap-southeast-1.elasticbeanstalk.com/graphql'", "gen:type:prod": "npm run gen:clean && apollo client:codegen __generated__ --target=typescript --includes 'src/**/*.{ts,tsx}' --endpoint 'https://server.matters.news/graphql'", "gen:watch": "npm run gen:type -- --watch", "gen:watch:prod": "npm run gen:type:prod -- --watch", diff --git a/src/components/BlockUser/Button/index.tsx b/src/components/BlockUser/Button/index.tsx index 0359331b2a..c1b0eaff78 100644 --- a/src/components/BlockUser/Button/index.tsx +++ b/src/components/BlockUser/Button/index.tsx @@ -6,11 +6,11 @@ import { Translate, } from '~/components' import { useMutation } from '~/components/GQL' -import UNBLOCK_USER from '~/components/GQL/mutations/unblockUser' +import TOGGLE_BLOCK_USER from '~/components/GQL/mutations/toggleBlockUser' import { ADD_TOAST } from '~/common/enums' -import { UnblockUser } from '~/components/GQL/mutations/__generated__/UnblockUser' +import { ToggleBlockUser } from '~/components/GQL/mutations/__generated__/ToggleBlockUser' import { BlockUserPrivate } from '../__generated__/BlockUserPrivate' import { BlockUserPublic } from '../__generated__/BlockUserPublic' @@ -21,10 +21,10 @@ const BlockUserButton = ({ user: BlockUserPublic & Partial openDialog: () => void }) => { - const [unblockUser] = useMutation(UNBLOCK_USER, { - variables: { id: user.id }, + const [unblockUser] = useMutation(TOGGLE_BLOCK_USER, { + variables: { id: user.id, enabled: false }, optimisticResponse: { - unblockUser: { + toggleBlockUser: { id: user.id, isBlocked: false, __typename: 'User', diff --git a/src/components/BlockUser/Dialog/index.tsx b/src/components/BlockUser/Dialog/index.tsx index 59606398a0..3a2fc6edb8 100644 --- a/src/components/BlockUser/Dialog/index.tsx +++ b/src/components/BlockUser/Dialog/index.tsx @@ -2,13 +2,13 @@ import { useState } from 'react' import { Dialog, Translate } from '~/components' import { useMutation } from '~/components/GQL' -import BLOCK_USER from '~/components/GQL/mutations/blockUser' +import TOGGLE_BLOCK_USER from '~/components/GQL/mutations/toggleBlockUser' import { ADD_TOAST } from '~/common/enums' import ViewBlocksButton from './ViewBlocksButton' -import { BlockUser as BlockUserMutate } from '~/components/GQL/mutations/__generated__/BlockUser' +import { ToggleBlockUser } from '~/components/GQL/mutations/__generated__/ToggleBlockUser' import { BlockUserPrivate } from '../__generated__/BlockUserPrivate' import { BlockUserPublic } from '../__generated__/BlockUserPublic' @@ -22,10 +22,10 @@ const BlockUserDialog = ({ user, children }: BlockUserDialogProps) => { const open = () => setShowDialog(true) const close = () => setShowDialog(false) - const [blockUser] = useMutation(BLOCK_USER, { - variables: { id: user.id }, + const [blockUser] = useMutation(TOGGLE_BLOCK_USER, { + variables: { id: user.id, enabled: true }, optimisticResponse: { - blockUser: { + toggleBlockUser: { id: user.id, isBlocked: true, __typename: 'User', diff --git a/src/components/Buttons/Bookmark/Subscribe.tsx b/src/components/Buttons/Bookmark/Subscribe.tsx index 7c219260e3..3afb1a21c8 100644 --- a/src/components/Buttons/Bookmark/Subscribe.tsx +++ b/src/components/Buttons/Bookmark/Subscribe.tsx @@ -31,7 +31,7 @@ const Subscribe = ({ articleId, size, disabled, inCard }: SubscribeProps) => { const [subscribe] = useMutation( TOGGLE_SUBSCRIBE_ARTICLE, { - variables: { id: articleId }, + variables: { id: articleId, enabled: true }, optimisticResponse: articleId ? { toggleSubscribeArticle: { diff --git a/src/components/Buttons/Bookmark/Unsubscribe.tsx b/src/components/Buttons/Bookmark/Unsubscribe.tsx index c7b1598d71..b14555992a 100644 --- a/src/components/Buttons/Bookmark/Unsubscribe.tsx +++ b/src/components/Buttons/Bookmark/Unsubscribe.tsx @@ -21,7 +21,7 @@ const Unsubscribe = ({ const [unsubscribe] = useMutation( TOGGLE_SUBSCRIBE_ARTICLE, { - variables: { id: articleId }, + variables: { id: articleId, enabled: false }, optimisticResponse: articleId ? { toggleSubscribeArticle: { diff --git a/src/components/Buttons/Follow/Follow.tsx b/src/components/Buttons/Follow/Follow.tsx index 3d2c92d8a5..3b331c5089 100644 --- a/src/components/Buttons/Follow/Follow.tsx +++ b/src/components/Buttons/Follow/Follow.tsx @@ -1,4 +1,3 @@ -import gql from 'graphql-tag' import _get from 'lodash/get' import _isNil from 'lodash/isNil' @@ -10,32 +9,23 @@ import { Translate, } from '~/components' import { useMutation } from '~/components/GQL' +import TOGGLE_FOLLOW_USER from '~/components/GQL/mutations/toggleFollowUser' import updateUserFollowerCount from '~/components/GQL/updates/userFollowerCount' import updateViewerFolloweeCount from '~/components/GQL/updates/viewerFolloweeCount' import { FollowButtonSize } from './index' +import { ToggleFollowUser } from '~/components/GQL/mutations/__generated__/ToggleFollowUser' import { FollowButtonUserPrivate } from './__generated__/FollowButtonUserPrivate' -import { FollowUser } from './__generated__/FollowUser' interface FollowProps { user: Partial size: FollowButtonSize } -const FOLLOW_USER = gql` - mutation FollowUser($id: ID!) { - toggleFollowUser(input: { id: $id }) { - id - isFollowee - isFollower - } - } -` - const Follow = ({ user, size }: FollowProps) => { - const [follow] = useMutation(FOLLOW_USER, { - variables: { id: user.id }, + const [follow] = useMutation(TOGGLE_FOLLOW_USER, { + variables: { id: user.id, enabled: true }, optimisticResponse: !_isNil(user.id) && !_isNil(user.isFollower) ? { diff --git a/src/components/Buttons/Follow/Unfollow.tsx b/src/components/Buttons/Follow/Unfollow.tsx index 16ef172541..672068c42b 100644 --- a/src/components/Buttons/Follow/Unfollow.tsx +++ b/src/components/Buttons/Follow/Unfollow.tsx @@ -1,4 +1,3 @@ -import gql from 'graphql-tag' import _get from 'lodash/get' import _isNil from 'lodash/isNil' import { useState } from 'react' @@ -11,33 +10,24 @@ import { Translate, } from '~/components' import { useMutation } from '~/components/GQL' +import TOGGLE_FOLLOW_USER from '~/components/GQL/mutations/toggleFollowUser' import updateUserFollowerCount from '~/components/GQL/updates/userFollowerCount' import updateViewerFolloweeCount from '~/components/GQL/updates/viewerFolloweeCount' import { FollowButtonSize } from './index' +import { ToggleFollowUser } from '~/components/GQL/mutations/__generated__/ToggleFollowUser' import { FollowButtonUserPrivate } from './__generated__/FollowButtonUserPrivate' -import { UnfollowUser } from './__generated__/UnfollowUser' interface UnfollowProps { user: Partial size: FollowButtonSize } -const UNFOLLOW_USER = gql` - mutation UnfollowUser($id: ID!) { - toggleFollowUser(input: { id: $id }) { - id - isFollowee - isFollower - } - } -` - const Unfollow = ({ user, size }: UnfollowProps) => { const [hover, setHover] = useState(false) - const [unfollow] = useMutation(UNFOLLOW_USER, { - variables: { id: user.id }, + const [unfollow] = useMutation(TOGGLE_FOLLOW_USER, { + variables: { id: user.id, enabled: false }, optimisticResponse: !_isNil(user.id) && !_isNil(user.isFollower) ? { diff --git a/src/components/Buttons/UnblockUser/index.tsx b/src/components/Buttons/UnblockUser/index.tsx index d04d0406ac..0d599baf48 100644 --- a/src/components/Buttons/UnblockUser/index.tsx +++ b/src/components/Buttons/UnblockUser/index.tsx @@ -3,11 +3,11 @@ import _isNil from 'lodash/isNil' import { Button, TextIcon, Translate } from '~/components' import { useMutation } from '~/components/GQL' -import UNBLOCK_USER from '~/components/GQL/mutations/unblockUser' +import TOGGLE_BLOCK_USER from '~/components/GQL/mutations/toggleBlockUser' import { ADD_TOAST } from '~/common/enums' -import { UnblockUser } from '~/components/GQL/mutations/__generated__/UnblockUser' +import { ToggleBlockUser } from '~/components/GQL/mutations/__generated__/ToggleBlockUser' import { UnblockUserButtonUserPrivate } from './__generated__/UnblockUserButtonUserPrivate' interface UnblockUserButtonProps { @@ -26,11 +26,11 @@ const fragments = { } export const UnblockUserButton = ({ user }: UnblockUserButtonProps) => { - const [unblockUser] = useMutation(UNBLOCK_USER, { - variables: { id: user.id }, + const [unblockUser] = useMutation(TOGGLE_BLOCK_USER, { + variables: { id: user.id, enabled: false }, optimisticResponse: !_isNil(user.id) ? { - unblockUser: { + toggleBlockUser: { id: user.id, isBlocked: false, __typename: 'User', diff --git a/src/components/Comment/DropdownActions/PinButton.tsx b/src/components/Comment/DropdownActions/PinButton.tsx index ddff6efe46..cf62a616db 100644 --- a/src/components/Comment/DropdownActions/PinButton.tsx +++ b/src/components/Comment/DropdownActions/PinButton.tsx @@ -8,36 +8,10 @@ import { Translate, } from '~/components' import { useMutation } from '~/components/GQL' +import TOGGLE_PIN_COMMENT from '~/components/GQL/mutations/togglePinComment' +import { TogglePinComment } from '~/components/GQL/mutations/__generated__/TogglePinComment' import { PinButtonComment } from './__generated__/PinButtonComment' -import { PinComment } from './__generated__/PinComment' -import { UnpinComment } from './__generated__/UnpinComment' - -const PIN_COMMENT = gql` - mutation PinComment($id: ID!) { - pinComment(input: { id: $id }) { - id - pinned - article { - id - pinCommentLeft - } - } - } -` - -const UNPIN_COMMENT = gql` - mutation UnpinComment($id: ID!) { - unpinComment(input: { id: $id }) { - id - pinned - article { - id - pinCommentLeft - } - } - } -` const fragments = { comment: gql` @@ -54,10 +28,10 @@ const fragments = { const PinButton = ({ comment }: { comment: PinButtonComment }) => { const canPin = comment.article.pinCommentLeft > 0 - const [unpinComment] = useMutation(UNPIN_COMMENT, { - variables: { id: comment.id }, + const [unpinComment] = useMutation(TOGGLE_PIN_COMMENT, { + variables: { id: comment.id, enabled: false }, optimisticResponse: { - unpinComment: { + togglePinComment: { id: comment.id, pinned: false, article: { @@ -67,10 +41,10 @@ const PinButton = ({ comment }: { comment: PinButtonComment }) => { }, }, }) - const [pinComment] = useMutation(PIN_COMMENT, { - variables: { id: comment.id }, + const [pinComment] = useMutation(TOGGLE_PIN_COMMENT, { + variables: { id: comment.id, enabled: true }, optimisticResponse: { - pinComment: { + togglePinComment: { id: comment.id, pinned: true, article: { diff --git a/src/components/GQL/mutations/blockUser.ts b/src/components/GQL/mutations/blockUser.ts deleted file mode 100644 index ce44ca61b2..0000000000 --- a/src/components/GQL/mutations/blockUser.ts +++ /dev/null @@ -1,10 +0,0 @@ -import gql from 'graphql-tag' - -export default gql` - mutation BlockUser($id: ID!) { - blockUser(input: { id: $id }) { - id - isBlocked - } - } -` diff --git a/src/components/GQL/mutations/toggleBlockUser.ts b/src/components/GQL/mutations/toggleBlockUser.ts new file mode 100644 index 0000000000..e823676324 --- /dev/null +++ b/src/components/GQL/mutations/toggleBlockUser.ts @@ -0,0 +1,10 @@ +import gql from 'graphql-tag' + +export default gql` + mutation ToggleBlockUser($id: ID!, $enabled: Boolean) { + toggleBlockUser(input: { id: $id, enabled: $enabled }) { + id + isBlocked + } + } +` diff --git a/src/components/GQL/mutations/toggleFollowTag.ts b/src/components/GQL/mutations/toggleFollowTag.ts new file mode 100644 index 0000000000..9cf4224cae --- /dev/null +++ b/src/components/GQL/mutations/toggleFollowTag.ts @@ -0,0 +1,10 @@ +import gql from 'graphql-tag' + +export default gql` + mutation ToggleFollowTag($id: ID!, $enabled: Boolean) { + toggleFollowTag(input: { id: $id, enabled: $enabled }) { + id + isFollower + } + } +` diff --git a/src/components/GQL/mutations/toggleFollowUser.ts b/src/components/GQL/mutations/toggleFollowUser.ts new file mode 100644 index 0000000000..31ee472d29 --- /dev/null +++ b/src/components/GQL/mutations/toggleFollowUser.ts @@ -0,0 +1,11 @@ +import gql from 'graphql-tag' + +export default gql` + mutation ToggleFollowUser($id: ID!, $enabled: Boolean) { + toggleFollowUser(input: { id: $id, enabled: $enabled }) { + id + isFollowee + isFollower + } + } +` diff --git a/src/components/GQL/mutations/togglePinComment.ts b/src/components/GQL/mutations/togglePinComment.ts new file mode 100644 index 0000000000..7bbac6aa3c --- /dev/null +++ b/src/components/GQL/mutations/togglePinComment.ts @@ -0,0 +1,14 @@ +import gql from 'graphql-tag' + +export default gql` + mutation TogglePinComment($id: ID!, $enabled: Boolean) { + togglePinComment(input: { id: $id, enabled: $enabled }) { + id + pinned + article { + id + pinCommentLeft + } + } + } +` diff --git a/src/components/GQL/mutations/toggleSubscribeArticle.ts b/src/components/GQL/mutations/toggleSubscribeArticle.ts index 551ad4d95e..9f0948275d 100644 --- a/src/components/GQL/mutations/toggleSubscribeArticle.ts +++ b/src/components/GQL/mutations/toggleSubscribeArticle.ts @@ -1,8 +1,8 @@ import gql from 'graphql-tag' export default gql` - mutation ToggleSubscribeArticle($id: ID!) { - toggleSubscribeArticle(input: { id: $id }) { + mutation ToggleSubscribeArticle($id: ID!, $enabled: Boolean) { + toggleSubscribeArticle(input: { id: $id, enabled: $enabled }) { id subscribed } diff --git a/src/components/GQL/mutations/unblockUser.ts b/src/components/GQL/mutations/unblockUser.ts deleted file mode 100644 index f753ecb950..0000000000 --- a/src/components/GQL/mutations/unblockUser.ts +++ /dev/null @@ -1,10 +0,0 @@ -import gql from 'graphql-tag' - -export default gql` - mutation UnblockUser($id: ID!) { - unblockUser(input: { id: $id }) { - id - isBlocked - } - } -` diff --git a/src/components/PushInitializer/index.tsx b/src/components/PushInitializer/index.tsx index 6520b197ad..06a1c8a72d 100644 --- a/src/components/PushInitializer/index.tsx +++ b/src/components/PushInitializer/index.tsx @@ -9,8 +9,10 @@ const PushInitializer = ({ client }: { client: ApolloClient }) => { const viewer = useContext(ViewerContext) useEffect(() => { - initializePush({ client, viewer }) - }, []) + if (viewer.privateFetched) { + initializePush({ client, viewer }) + } + }, [viewer.privateFetched]) return null } diff --git a/src/views/Home/Feed/index.tsx b/src/views/Home/Feed/index.tsx index 254f5d00ae..1c02f2ad32 100644 --- a/src/views/Home/Feed/index.tsx +++ b/src/views/Home/Feed/index.tsx @@ -132,7 +132,7 @@ const MainFeed = ({ feedSortType: sortBy, viewMode }: MainFeedProps) => { const fetchedPrviateSortsRef = useRef([]) useEffect(() => { const fetched = fetchedPrviateSortsRef.current.indexOf(sortBy) >= 0 - if (loading || !edges || fetched) { + if (loading || !edges || fetched || !viewer.id) { return } diff --git a/src/views/TagDetail/Buttons/FollowButton/Follow.tsx b/src/views/TagDetail/Buttons/FollowButton/Follow.tsx index 2f0f1624b6..3930b67e7a 100644 --- a/src/views/TagDetail/Buttons/FollowButton/Follow.tsx +++ b/src/views/TagDetail/Buttons/FollowButton/Follow.tsx @@ -1,4 +1,3 @@ -import gql from 'graphql-tag' import _isNil from 'lodash/isNil' import { useContext } from 'react' @@ -10,28 +9,20 @@ import { ViewerContext, } from '~/components' import { useMutation } from '~/components/GQL' +import TOGGLE_FOLLOW_TAG from '~/components/GQL/mutations/toggleFollowTag' import updateTagFollowers from '~/components/GQL/updates/tagFollowers' +import { ToggleFollowTag } from '~/components/GQL/mutations/__generated__/ToggleFollowTag' import { FollowButtonTag as FollowButtonTagType } from './__generated__/FollowButtonTag' -import { FollowTag } from './__generated__/FollowTag' interface FollowProps { tag: FollowButtonTagType } -const FOLLOW_TAG = gql` - mutation FollowTag($id: ID!) { - toggleFollowTag(input: { id: $id }) { - id - isFollower - } - } -` - const Follow = ({ tag }: FollowProps) => { const viewer = useContext(ViewerContext) - const [follow] = useMutation(FOLLOW_TAG, { - variables: { id: tag.id }, + const [follow] = useMutation(TOGGLE_FOLLOW_TAG, { + variables: { id: tag.id, enabled: true }, optimisticResponse: !_isNil(tag.id) && !_isNil(tag.isFollower) ? { diff --git a/src/views/TagDetail/Buttons/FollowButton/Unfollow.tsx b/src/views/TagDetail/Buttons/FollowButton/Unfollow.tsx index 83765fd3a2..92b8900493 100644 --- a/src/views/TagDetail/Buttons/FollowButton/Unfollow.tsx +++ b/src/views/TagDetail/Buttons/FollowButton/Unfollow.tsx @@ -1,32 +1,23 @@ -import gql from 'graphql-tag' import _isNil from 'lodash/isNil' import { useContext, useState } from 'react' import { Button, TextIcon, Translate, ViewerContext } from '~/components' import { useMutation } from '~/components/GQL' +import TOGGLE_FOLLOW_TAG from '~/components/GQL/mutations/toggleFollowTag' import updateTagFollowers from '~/components/GQL/updates/tagFollowers' +import { ToggleFollowTag } from '~/components/GQL/mutations/__generated__/ToggleFollowTag' import { FollowButtonTag as FollowButtonTagType } from './__generated__/FollowButtonTag' -import { UnfollowTag } from './__generated__/UnfollowTag' interface UnfollowTagProps { tag: FollowButtonTagType } -const UNFOLLOW_TAG = gql` - mutation UnfollowTag($id: ID!) { - toggleFollowTag(input: { id: $id }) { - id - isFollower - } - } -` - const Unfollow = ({ tag }: UnfollowTagProps) => { const viewer = useContext(ViewerContext) const [hover, setHover] = useState(false) - const [unfollow] = useMutation(UNFOLLOW_TAG, { - variables: { id: tag.id }, + const [unfollow] = useMutation(TOGGLE_FOLLOW_TAG, { + variables: { id: tag.id, enabled: false }, optimisticResponse: !_isNil(tag.id) && !_isNil(tag.isFollower) ? { From f4de0558699377bc3ecb4a72f9e0569560a8e14a Mon Sep 17 00:00:00 2001 From: RoberTu Date: Fri, 17 Jul 2020 15:57:30 +0800 Subject: [PATCH 60/86] chore(types): revert gen:type endpoint --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 792055aacb..348277f8e6 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "analyze": "BUNDLE_ANALYZE=both next build", "analyze:server": "BUNDLE_ANALYZE=server next build", "analyze:browser": "BUNDLE_ANALYZE=browser next build", - "gen:type": "npm run gen:clean && apollo client:codegen __generated__ --target=typescript --includes 'src/**/*.{ts,tsx}' --endpoint 'http://matters-server-develop.ap-southeast-1.elasticbeanstalk.com/graphql'", + "gen:type": "npm run gen:clean && apollo client:codegen __generated__ --target=typescript --includes 'src/**/*.{ts,tsx}' --endpoint 'https://server-stage.matters.news/graphql'", "gen:type:prod": "npm run gen:clean && apollo client:codegen __generated__ --target=typescript --includes 'src/**/*.{ts,tsx}' --endpoint 'https://server.matters.news/graphql'", "gen:watch": "npm run gen:type -- --watch", "gen:watch:prod": "npm run gen:type:prod -- --watch", From 6c4aeba2128172b48d7e35b99faa81a7b844a253 Mon Sep 17 00:00:00 2001 From: RoberTu Date: Fri, 17 Jul 2020 18:40:09 +0800 Subject: [PATCH 61/86] feat(public-private-queries): loadPrivate after refetch public data --- src/components/GQL/queries/followeeCount.ts | 12 ------------ .../GQL/updates/viewerFolloweeCount.ts | 16 +++++++++++++--- .../Responses/FeaturedComments/index.tsx | 7 ++++++- .../Responses/LatestResponses/index.tsx | 17 +++++++++++++---- src/views/ArticleDetail/index.tsx | 11 ++++++++--- src/views/Authors/index.tsx | 17 ++++++++++++++--- .../Follow/PickAuthors/AuthorPicker/index.tsx | 2 +- src/views/Home/Feed/Authors/index.tsx | 15 +++++++-------- src/views/Home/Feed/index.tsx | 8 +++++++- src/views/Home/Sidebar/Authors/index.tsx | 10 +++++----- src/views/TagDetail/Articles/index.tsx | 15 +++++++++++---- src/views/Tags/index.tsx | 8 ++++---- src/views/User/Articles/UserArticles.tsx | 19 +++++++++++++++---- src/views/User/Comments/UserComments.tsx | 17 +++++++++++++---- src/views/User/Followees/UserFollowees.tsx | 17 +++++++++++++---- src/views/User/Followers/UserFollowers.tsx | 17 +++++++++++++---- 16 files changed, 143 insertions(+), 65 deletions(-) delete mode 100644 src/components/GQL/queries/followeeCount.ts diff --git a/src/components/GQL/queries/followeeCount.ts b/src/components/GQL/queries/followeeCount.ts deleted file mode 100644 index 99537d03c0..0000000000 --- a/src/components/GQL/queries/followeeCount.ts +++ /dev/null @@ -1,12 +0,0 @@ -import gql from 'graphql-tag' - -export default gql` - query ViewerFolloweeCount { - viewer { - id - followees(input: { first: 0 }) { - totalCount - } - } - } -` diff --git a/src/components/GQL/updates/viewerFolloweeCount.ts b/src/components/GQL/updates/viewerFolloweeCount.ts index fd0c004f79..3e71e5767f 100644 --- a/src/components/GQL/updates/viewerFolloweeCount.ts +++ b/src/components/GQL/updates/viewerFolloweeCount.ts @@ -1,10 +1,20 @@ import { DataProxy } from 'apollo-cache' - -import VIEWER_FOLLOWEE_COUNT from '~/components/GQL/queries/followeeCount' +import gql from 'graphql-tag' import { ERROR_CODES } from '~/common/enums' -import { ViewerFolloweeCount } from '~/components/GQL/queries/__generated__/ViewerFolloweeCount' +import { ViewerFolloweeCount } from './__generated__/ViewerFolloweeCount' + +const VIEWER_FOLLOWEE_COUNT = gql` + query ViewerFolloweeCount { + viewer { + id + followees(input: { first: 0 }) { + totalCount + } + } + } +` const update = ({ cache, diff --git a/src/views/ArticleDetail/Responses/FeaturedComments/index.tsx b/src/views/ArticleDetail/Responses/FeaturedComments/index.tsx index bb23d38155..66f615fd66 100644 --- a/src/views/ArticleDetail/Responses/FeaturedComments/index.tsx +++ b/src/views/ArticleDetail/Responses/FeaturedComments/index.tsx @@ -37,7 +37,7 @@ const FeaturedComments = () => { * Data Fetching */ // public data - const { data, loading, fetchMore, refetch, client } = useQuery< + const { data, loading, fetchMore, refetch: refetchPublic, client } = useQuery< FeaturedCommentsPublic >(FEATURED_COMMENTS_PUBLIC, { variables: { mediaHash }, @@ -94,6 +94,11 @@ const FeaturedComments = () => { loadPrivate(newData) } + // refetch & pull to refresh + const refetch = async () => { + const { data: newData } = await refetchPublic() + loadPrivate(newData) + } usePullToRefresh.Handler(refetch) if (loading && !data) { diff --git a/src/views/ArticleDetail/Responses/LatestResponses/index.tsx b/src/views/ArticleDetail/Responses/LatestResponses/index.tsx index 99d51c1c1d..b217c4678c 100644 --- a/src/views/ArticleDetail/Responses/LatestResponses/index.tsx +++ b/src/views/ArticleDetail/Responses/LatestResponses/index.tsx @@ -75,9 +75,14 @@ const LatestResponses = () => { * Data Fetching */ // public data - const { data, loading, error, fetchMore, refetch, client } = useQuery< - LatestResponsesPublic - >(LATEST_RESPONSES_PUBLIC, { + const { + data, + loading, + error, + fetchMore, + refetch: refetchPublic, + client, + } = useQuery(LATEST_RESPONSES_PUBLIC, { variables: { mediaHash, first: RESPONSES_COUNT, @@ -145,7 +150,11 @@ const LatestResponses = () => { loadPrivate(newData) } - // refetch when comment is sent or pull down + // refetch & pull to refresh + const refetch = async () => { + const { data: newData } = await refetchPublic() + loadPrivate(newData) + } useEventListener(REFETCH_RESPONSES, refetch) usePullToRefresh.Handler(refetch) diff --git a/src/views/ArticleDetail/index.tsx b/src/views/ArticleDetail/index.tsx index 882ae6d6ca..0d48bd227e 100644 --- a/src/views/ArticleDetail/index.tsx +++ b/src/views/ArticleDetail/index.tsx @@ -99,7 +99,7 @@ const ArticleDetail = () => { const canEdit = isAuthor && !viewer.isInactive // fetch private data - useEffect(() => { + const loadPrivate = () => { if (!viewer.id || !article) { return } @@ -112,6 +112,10 @@ const ArticleDetail = () => { includeContent: article.state !== 'active' && isAuthor, }, }) + } + + useEffect(() => { + loadPrivate() }, [mediaHash, viewer.id, article]) // translation @@ -158,9 +162,10 @@ const ArticleDetail = () => { const [editModeCollection, setEditModeCollection] = useState< ArticleDigestDropdownArticle[] >([]) - const onEditSaved = () => { + const onEditSaved = async () => { setEditMode(false) - refetchPublic() + await refetchPublic() + loadPrivate() } useEffect(() => { diff --git a/src/views/Authors/index.tsx b/src/views/Authors/index.tsx index 263e276395..707dd33173 100644 --- a/src/views/Authors/index.tsx +++ b/src/views/Authors/index.tsx @@ -27,9 +27,14 @@ const Authors = () => { * Data Fetching */ // public data - const { data, loading, error, fetchMore, refetch, client } = useQuery< - AllAuthorsPublic - >(ALL_AUTHORS_PUBLIC) + const { + data, + loading, + error, + fetchMore, + refetch: refetchPublic, + client, + } = useQuery(ALL_AUTHORS_PUBLIC) // pagination const connectionPath = 'viewer.recommendation.authors' @@ -79,6 +84,12 @@ const Authors = () => { loadPrivate(newData) } + // refetch & pull to refresh + const refetch = async () => { + const { data: newData } = await refetchPublic() + loadPrivate(newData) + } + /** * Render */ diff --git a/src/views/Follow/PickAuthors/AuthorPicker/index.tsx b/src/views/Follow/PickAuthors/AuthorPicker/index.tsx index a6b3de15dd..d773869d7d 100644 --- a/src/views/Follow/PickAuthors/AuthorPicker/index.tsx +++ b/src/views/Follow/PickAuthors/AuthorPicker/index.tsx @@ -59,7 +59,7 @@ export const AuthorPicker = ({ title }: { title: React.ReactNode }) => { size={[null, '1.25rem']} spacing={[0, 'xtight']} bgActiveColor="grey-lighter" - onClick={() => refetch()} + onClick={refetch} > } color="grey"> diff --git a/src/views/Home/Feed/Authors/index.tsx b/src/views/Home/Feed/Authors/index.tsx index bd562d067c..d0c0857bee 100644 --- a/src/views/Home/Feed/Authors/index.tsx +++ b/src/views/Home/Feed/Authors/index.tsx @@ -28,10 +28,9 @@ const FeedAuthors = () => { * Data Fetching */ // public data - const { data, loading, error, refetch, client } = useQuery( - FEED_AUTHORS_PUBLIC, - { notifyOnNetworkStatusChange: true } - ) + const { data, loading, error, refetch: refetchPublic, client } = useQuery< + FeedAuthorsPublic + >(FEED_AUTHORS_PUBLIC, { notifyOnNetworkStatusChange: true }) const edges = data?.viewer?.recommendation.authors.edges @@ -60,9 +59,9 @@ const FeedAuthors = () => { loadPrivate(data) }, [!!edges, viewer.id]) - // fetch - const suffle = async () => { - const { data: newData } = await refetch() + // refetch & pull to refresh + const refetch = async () => { + const { data: newData } = await refetchPublic() loadPrivate(newData) } @@ -82,7 +81,7 @@ const FeedAuthors = () => { size={[null, '1.25rem']} spacing={[0, 'xtight']} bgActiveColor="grey-lighter" - onClick={suffle} + onClick={refetch} > } diff --git a/src/views/Home/Feed/index.tsx b/src/views/Home/Feed/index.tsx index 1c02f2ad32..21a0e5bf71 100644 --- a/src/views/Home/Feed/index.tsx +++ b/src/views/Home/Feed/index.tsx @@ -100,7 +100,7 @@ const MainFeed = ({ feedSortType: sortBy, viewMode }: MainFeedProps) => { loading, fetchMore, networkStatus, - refetch, + refetch: refetchPublic, client, } = useQuery(FEED_ARTICLES_PUBLIC[sortBy], { notifyOnNetworkStatusChange: true, @@ -167,6 +167,12 @@ const MainFeed = ({ feedSortType: sortBy, viewMode }: MainFeedProps) => { loadPrivate(newData) } + // refetch & pull to refresh + const refetch = async () => { + const { data: newData } = await refetchPublic() + loadPrivate(newData) + } + /** * Render */ diff --git a/src/views/Home/Sidebar/Authors/index.tsx b/src/views/Home/Sidebar/Authors/index.tsx index 1af80381a1..459d3fdbf6 100644 --- a/src/views/Home/Sidebar/Authors/index.tsx +++ b/src/views/Home/Sidebar/Authors/index.tsx @@ -27,7 +27,7 @@ const Authors = () => { * Data Fetching */ // public data - const { data, loading, error, refetch, client } = useQuery< + const { data, loading, error, refetch: refetchPublic, client } = useQuery< SidebarAuthorsPublic >(SIDEBAR_AUTHORS_PUBLIC, { notifyOnNetworkStatusChange: true }) const edges = data?.viewer?.recommendation.authors.edges @@ -57,9 +57,9 @@ const Authors = () => { loadPrivate(data) }, [!!edges, viewer.id]) - // fetch - const suffle = async () => { - const { data: newData } = await refetch() + // refetch + const refetch = async () => { + const { data: newData } = await refetchPublic() loadPrivate(newData) } @@ -80,7 +80,7 @@ const Authors = () => { size={[null, '1.25rem']} spacing={[0, 'xtight']} bgActiveColor="grey-lighter" - onClick={suffle} + onClick={refetch} > } diff --git a/src/views/TagDetail/Articles/index.tsx b/src/views/TagDetail/Articles/index.tsx index 9c0824f2ff..15ed2c20c9 100644 --- a/src/views/TagDetail/Articles/index.tsx +++ b/src/views/TagDetail/Articles/index.tsx @@ -40,7 +40,7 @@ const TagDetailArticles = ({ tagId, selected }: TagArticlesProps) => { loading, error, fetchMore, - refetch, + refetch: refetchPublic, networkStatus, client, } = useQuery(TAG_ARTICLES_PUBLIC, { @@ -108,7 +108,12 @@ const TagDetailArticles = ({ tagId, selected }: TagArticlesProps) => { } // refetch, sync & pull to refresh - const sync = ({ + const refetch = async () => { + const { data: newData } = await refetchPublic() + loadPrivate(newData) + } + + const sync = async ({ event, differences = 0, }: { @@ -118,20 +123,22 @@ const TagDetailArticles = ({ tagId, selected }: TagArticlesProps) => { const count = (edges || []).length switch (event) { case 'add': - refetch({ + const { data: addData } = await refetchPublic({ variables: { id: tagId, first: count + differences, }, }) + loadPrivate(addData) break case 'delete': - refetch({ + const { data: deleteData } = await refetchPublic({ variables: { id: tagId, first: Math.max(count - 1, 0), }, }) + loadPrivate(deleteData) break } } diff --git a/src/views/Tags/index.tsx b/src/views/Tags/index.tsx index 450c20e8ea..b95a6aba9f 100644 --- a/src/views/Tags/index.tsx +++ b/src/views/Tags/index.tsx @@ -22,11 +22,11 @@ import { QueryError } from '~/components/GQL' import { analytics, mergeConnections, toPath } from '~/common/utils' -import { AllTags } from './__generated__/AllTags' +import { AllTagsPublic } from './__generated__/AllTagsPublic' const ALL_TAGS = gql` - query AllTags($after: String) { - viewer { + query AllTagsPublic($after: String) { + viewer @connection(key: "viewerTags") { id recommendation { tags(input: { first: 20, after: $after }) { @@ -74,7 +74,7 @@ const CreateTagButton = () => { } const Tags = () => { - const { data, loading, error, fetchMore, refetch } = useQuery( + const { data, loading, error, fetchMore, refetch } = useQuery( ALL_TAGS ) diff --git a/src/views/User/Articles/UserArticles.tsx b/src/views/User/Articles/UserArticles.tsx index 182644c104..beb15f2a01 100644 --- a/src/views/User/Articles/UserArticles.tsx +++ b/src/views/User/Articles/UserArticles.tsx @@ -64,9 +64,16 @@ const UserArticles = () => { * Data Fetching */ // public data - const { data, loading, error, fetchMore, refetch, client } = useQuery< - UserArticlesPublic - >(USER_ARTICLES_PUBLIC, { variables: { userName } }) + const { + data, + loading, + error, + fetchMore, + refetch: refetchPublic, + client, + } = useQuery(USER_ARTICLES_PUBLIC, { + variables: { userName }, + }) // pagination const connectionPath = 'user.articles' @@ -116,7 +123,11 @@ const UserArticles = () => { loadPrivate(newData) } - // pull to refresh + // refetch & pull to refresh + const refetch = async () => { + const { data: newData } = await refetchPublic() + loadPrivate(newData) + } usePullToRefresh.Register() usePullToRefresh.Handler(refetch) diff --git a/src/views/User/Comments/UserComments.tsx b/src/views/User/Comments/UserComments.tsx index e95dc73dbd..781277a624 100644 --- a/src/views/User/Comments/UserComments.tsx +++ b/src/views/User/Comments/UserComments.tsx @@ -81,9 +81,14 @@ const BaseUserComments = ({ user }: UserIdUser) => { * Data Fetching */ // public data - const { data, loading, error, fetchMore, refetch, client } = useQuery< - UserCommentsPublic - >(USER_COMMENTS_PUBLIC, { + const { + data, + loading, + error, + fetchMore, + refetch: refetchPublic, + client, + } = useQuery(USER_COMMENTS_PUBLIC, { variables: { id: user?.id }, }) @@ -141,7 +146,11 @@ const BaseUserComments = ({ user }: UserIdUser) => { loadPrivate(newData) } - // pull to refresh + // refetch & pull to refresh + const refetch = async () => { + const { data: newData } = await refetchPublic() + loadPrivate(newData) + } usePullToRefresh.Register() usePullToRefresh.Handler(refetch) diff --git a/src/views/User/Followees/UserFollowees.tsx b/src/views/User/Followees/UserFollowees.tsx index 18313fdf09..d0ed7b8099 100644 --- a/src/views/User/Followees/UserFollowees.tsx +++ b/src/views/User/Followees/UserFollowees.tsx @@ -30,9 +30,14 @@ const UserFollowees = () => { * Data Fetching */ // public data - const { data, loading, error, fetchMore, refetch, client } = useQuery< - UserFolloweePublic - >(USER_FOLLOWEES_PUBLIC, { + const { + data, + loading, + error, + fetchMore, + refetch: refetchPublic, + client, + } = useQuery(USER_FOLLOWEES_PUBLIC, { variables: { userName }, }) @@ -83,7 +88,11 @@ const UserFollowees = () => { loadPrivate(newData) } - // pull to refresh + // refetch & pull to refresh + const refetch = async () => { + const { data: newData } = await refetchPublic() + loadPrivate(newData) + } usePullToRefresh.Register() usePullToRefresh.Handler(refetch) diff --git a/src/views/User/Followers/UserFollowers.tsx b/src/views/User/Followers/UserFollowers.tsx index 6fb5606fce..fa55f9190b 100644 --- a/src/views/User/Followers/UserFollowers.tsx +++ b/src/views/User/Followers/UserFollowers.tsx @@ -30,9 +30,14 @@ const UserFollowers = () => { * Data Fetching */ // public data - const { data, loading, error, fetchMore, refetch, client } = useQuery< - UserFollowerPublic - >(USER_FOLLOWERS_PUBLIC, { + const { + data, + loading, + error, + fetchMore, + refetch: refetchPublic, + client, + } = useQuery(USER_FOLLOWERS_PUBLIC, { variables: { userName }, }) @@ -83,7 +88,11 @@ const UserFollowers = () => { loadPrivate(newData) } - // pull to refresh + // refetch & pull to refresh + const refetch = async () => { + const { data: newData } = await refetchPublic() + loadPrivate(newData) + } usePullToRefresh.Register() usePullToRefresh.Handler(refetch) From d065b4c7c711330ca3629130059089406b8000ea Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sun, 19 Jul 2020 21:19:07 +0000 Subject: [PATCH 62/86] build(deps): bump firebase from 7.16.0 to 7.16.1 Bumps [firebase](https://github.com/firebase/firebase-js-sdk) from 7.16.0 to 7.16.1. - [Release notes](https://github.com/firebase/firebase-js-sdk/releases) - [Commits](https://github.com/firebase/firebase-js-sdk/compare/firebase@7.16.0...firebase@7.16.1) Signed-off-by: dependabot-preview[bot] --- package-lock.json | 66 +++++++++++++++++++++++------------------------ package.json | 2 +- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/package-lock.json b/package-lock.json index be1c67176b..4f0b2821b8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "matters-web", - "version": "3.10.1", + "version": "3.10.2", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -1786,9 +1786,9 @@ "integrity": "sha512-L/ZnJRAq7F++utfuoTKX4CLBG5YR7tFO3PLzG1/oXXKEezJ0kRL3CMRoueBEmTCzVb/6SIs2Qlaw++uDgi5Xyg==" }, "@firebase/auth": { - "version": "0.14.8", - "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-0.14.8.tgz", - "integrity": "sha512-LufoWcFpYAoCUkFDKSELH69xI8NdOjNTUFKvWfADZN7ysr4dpPdDs2ZYnH67FqcMb0tX+Jdx6vWrF6VZ37AAJQ==", + "version": "0.14.9", + "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-0.14.9.tgz", + "integrity": "sha512-PxYa2r5qUEdheXTvqROFrMstK8W4uPiP7NVfp+2Bec+AjY5PxZapCx/YFDLkU0D7YBI82H74PtZrzdJZw7TJ4w==", "requires": { "@firebase/auth-types": "0.10.1" } @@ -1813,9 +1813,9 @@ } }, "@firebase/database": { - "version": "0.6.7", - "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.6.7.tgz", - "integrity": "sha512-vm0ch2zNSoHfXWnDG6WVjf0p/BdXOMBL1lAfkGu3DYH/Rkl4p97x57w0WNOURNfL4GY2LIqScSYKCidV7jqTog==", + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.6.8.tgz", + "integrity": "sha512-Psibz/LD9WBvZRS7A/kkYd5i5l6tBw49adSFmCM2ZJlKE9fxZhxay02AerwfXHiq3gPKVeqXUjBIRuHOWdEXmw==", "requires": { "@firebase/auth-interop-types": "0.1.5", "@firebase/component": "0.1.16", @@ -1835,9 +1835,9 @@ } }, "@firebase/firestore": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-1.16.0.tgz", - "integrity": "sha512-RsgGIP9e6HW5soEHIuo0CGVFpeTKe0hqjrgOLk92W0mjL6irzBlqmd5HcGMY4F5QiZryc2vMT1/3LvRhkUyf8g==", + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-1.16.1.tgz", + "integrity": "sha512-TGtvNIGHMEFFEuOSsRswou576GPZY39vXIsenn0B1Dqz9ACpyDtvAT9YdbG38srlPq7ZKwsP5x04LB43zZ6eAg==", "requires": { "@firebase/component": "0.1.16", "@firebase/firestore-types": "1.12.0", @@ -1970,20 +1970,20 @@ "integrity": "sha512-G96qnF3RYGbZsTRut7NBX0sxyczxt1uyCgXQuH/eAfUCngxjEGcZQnBdy6mvSdqdJh5mC31rWPO4v9/s7HwtzA==" }, "@firebase/storage": { - "version": "0.3.38", - "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.3.38.tgz", - "integrity": "sha512-gWVQr5xqrU3cfhhwbAE+9iJ0XMvzbxWMvteKurn5cRNaGbmSob/O/ISOAvsQgPnk+K9zPMd2OwyzaTOl9PEMrw==", + "version": "0.3.39", + "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.3.39.tgz", + "integrity": "sha512-uTE8kROU/NMas+0i2oK0U9LuAlDzt+Cis0ErmYPlbCvmFqpFdyu3TtlO5MYNoxGLaBjEyxb18NJZai9lNMXFlQ==", "requires": { "@firebase/component": "0.1.16", - "@firebase/storage-types": "0.3.12", + "@firebase/storage-types": "0.3.13", "@firebase/util": "0.2.50", "tslib": "^1.11.1" } }, "@firebase/storage-types": { - "version": "0.3.12", - "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.3.12.tgz", - "integrity": "sha512-DDV6Fs6aYoGw3w/zZZTkqiipxihnsvHf6znbeZYjIIHit3tr1uLJdGPDPiCTfZcTGPpg2ux6ZmvNDvVgJdHALw==" + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.3.13.tgz", + "integrity": "sha512-pL7b8d5kMNCCL0w9hF7pr16POyKkb3imOW7w0qYrhBnbyJTdVxMWZhb0HxCFyQWC0w3EiIFFmxoz8NTFZDEFog==" }, "@firebase/util": { "version": "0.2.50", @@ -2007,9 +2007,9 @@ } }, "@grpc/proto-loader": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.5.4.tgz", - "integrity": "sha512-HTM4QpI9B2XFkPz7pjwMyMgZchJ93TVkL3kWPW8GDMDKYxsMnmf4w2TNMJK7+KNiYHS5cJrCEAFlF+AwtXWVPA==", + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.5.5.tgz", + "integrity": "sha512-WwN9jVNdHRQoOBo9FDH7qU+mgfjPc8GygPYms3M+y3fbQLfnCe/Kv/E01t7JRgnrsOHH8euvSbed3mIalXhwqQ==", "requires": { "lodash.camelcase": "^4.3.0", "protobufjs": "^6.8.6" @@ -12511,23 +12511,23 @@ "integrity": "sha512-H1k/ESTD2rJ3liupyqWBPjZC+LKfCGixQzz/NDN4dkgbmG1bVFyMOh7luKSkVDoyfhgvRm62pviNMPI+eJTZcQ==" }, "firebase": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/firebase/-/firebase-7.16.0.tgz", - "integrity": "sha512-fYimLYkY0SS/jv4+ZnSp5u2+QdtwsTtLwPUfmWiKQnjqas1M5mqhQr3QB7vPQuSANhC3UJZZ5KPxLbFomMJLcA==", + "version": "7.16.1", + "resolved": "https://registry.npmjs.org/firebase/-/firebase-7.16.1.tgz", + "integrity": "sha512-mcvFh617lWPYnx6SmwgtwmliY8P3XBi8pm0LDY4a8WPD049goCMgmIEpKkX4R3gZ2noz2rVrxSUfodENPpttLg==", "requires": { "@firebase/analytics": "0.3.9", "@firebase/app": "0.6.8", "@firebase/app-types": "0.6.1", - "@firebase/auth": "0.14.8", - "@firebase/database": "0.6.7", - "@firebase/firestore": "1.16.0", + "@firebase/auth": "0.14.9", + "@firebase/database": "0.6.8", + "@firebase/firestore": "1.16.1", "@firebase/functions": "0.4.48", "@firebase/installations": "0.4.14", "@firebase/messaging": "0.6.20", "@firebase/performance": "0.3.9", "@firebase/polyfill": "0.3.36", "@firebase/remote-config": "0.1.25", - "@firebase/storage": "0.3.38", + "@firebase/storage": "0.3.39", "@firebase/util": "0.2.50" } }, @@ -21900,9 +21900,9 @@ "dev": true }, "protobufjs": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.9.0.tgz", - "integrity": "sha512-LlGVfEWDXoI/STstRDdZZKb/qusoAWUnmLg9R8OLSO473mBLWHowx8clbX5/+mKDEI+v7GzjoK9tRPZMMcoTrg==", + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.10.1.tgz", + "integrity": "sha512-pb8kTchL+1Ceg4lFd5XUpK8PdWacbvV5SK2ULH2ebrYtl4GjJmS24m6CKME67jzV53tbJxHlnNOSqQHbTsR9JQ==", "requires": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", @@ -21920,9 +21920,9 @@ }, "dependencies": { "@types/node": { - "version": "13.13.13", - "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.13.tgz", - "integrity": "sha512-UfvBE9oRCAJVzfR+3eWm/sdLFe/qroAPEXP3GPJ1SehQiEVgZT6NQZWYbPMiJ3UdcKM06v4j+S1lTcdWCmw+3g==" + "version": "13.13.14", + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.14.tgz", + "integrity": "sha512-Az3QsOt1U/K1pbCQ0TXGELTuTkPLOiFIQf3ILzbOyo0FqgV9SxRnxbxM5QlAveERZMHpZY+7u3Jz2tKyl+yg6g==" } } }, diff --git a/package.json b/package.json index 5618911ece..09a4e1e339 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "date-fns": "^2.14.0", "express": "^4.17.1", "fingerprintjs2": "^2.1.0", - "firebase": "^7.16.0", + "firebase": "^7.16.1", "formik": "^2.1.5", "graphql": "^14.7.0", "graphql-tag": "^2.10.4", From d7a4ac2f1bd9251c5c33f8790e2c8fd6683e9cc4 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sun, 19 Jul 2020 21:20:13 +0000 Subject: [PATCH 63/86] build(deps): bump date-fns from 2.14.0 to 2.15.0 Bumps [date-fns](https://github.com/date-fns/date-fns) from 2.14.0 to 2.15.0. - [Release notes](https://github.com/date-fns/date-fns/releases) - [Changelog](https://github.com/date-fns/date-fns/blob/master/CHANGELOG.md) - [Commits](https://github.com/date-fns/date-fns/compare/v2.14.0...v2.15.0) Signed-off-by: dependabot-preview[bot] --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index be1c67176b..28b4066d32 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "matters-web", - "version": "3.10.1", + "version": "3.10.2", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -10776,9 +10776,9 @@ } }, "date-fns": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.14.0.tgz", - "integrity": "sha512-1zD+68jhFgDIM0rF05rcwYO8cExdNqxjq4xP1QKM60Q45mnO6zaMWB4tOzrIr4M4GSLntsKeE4c9Bdl2jhL/yw==" + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.15.0.tgz", + "integrity": "sha512-ZCPzAMJZn3rNUvvQIMlXhDr4A+Ar07eLeGsGREoWU19a3Pqf5oYa+ccd+B3F6XVtQY6HANMFdOQ8A+ipFnvJdQ==" }, "deasync": { "version": "0.1.20", diff --git a/package.json b/package.json index 5618911ece..a815f37170 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "apollo-utilities": "^1.3.4", "autosize": "^4.0.2", "classnames": "^2.2.6", - "date-fns": "^2.14.0", + "date-fns": "^2.15.0", "express": "^4.17.1", "fingerprintjs2": "^2.1.0", "firebase": "^7.16.0", From be3e5440759850b3994b5bd8ed0674a6c3e05756 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sun, 19 Jul 2020 21:23:07 +0000 Subject: [PATCH 64/86] build(deps): bump react-focus-lock from 2.4.0 to 2.4.1 Bumps [react-focus-lock](https://github.com/theKashey/react-focus-lock) from 2.4.0 to 2.4.1. - [Release notes](https://github.com/theKashey/react-focus-lock/releases) - [Changelog](https://github.com/theKashey/react-focus-lock/blob/master/CHANGELOG.md) - [Commits](https://github.com/theKashey/react-focus-lock/compare/v2.4.0...v2.4.1) Signed-off-by: dependabot-preview[bot] --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index be1c67176b..70188a499b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "matters-web", - "version": "3.10.1", + "version": "3.10.2", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -22278,9 +22278,9 @@ "integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==" }, "react-focus-lock": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/react-focus-lock/-/react-focus-lock-2.4.0.tgz", - "integrity": "sha512-mue/boxdfNhfxnQcZtEBvqwZ5XQxk0uRoAMwLGl8j6XolFV3UIlt6iGFBGqRdJsvVHhtyKC5i8fkLnBidxCTbA==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/react-focus-lock/-/react-focus-lock-2.4.1.tgz", + "integrity": "sha512-c5ZP56KSpj9EAxzScTqQO7bQQNPltf/W1ZEBDqNDOV1XOIwvAyHX0O7db9ekiAtxyKgnqZjQlLppVg94fUeL9w==", "requires": { "@babel/runtime": "^7.0.0", "focus-lock": "^0.7.0", diff --git a/package.json b/package.json index 5618911ece..6653e58c2e 100644 --- a/package.json +++ b/package.json @@ -78,7 +78,7 @@ "react-beautiful-dnd": "^13.0.0", "react-copy-to-clipboard": "^5.0.2", "react-dom": "^16.13.1", - "react-focus-lock": "^2.4.0", + "react-focus-lock": "^2.4.1", "react-remove-scroll": "^2.3.0", "react-spring": "^9.0.0-rc.3", "react-use-gesture": "^7.0.15", From 20ea63d47ebd3a56c98783f652b6546a74182e17 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sun, 19 Jul 2020 21:25:02 +0000 Subject: [PATCH 65/86] build(deps-dev): bump @types/jest from 26.0.4 to 26.0.5 Bumps [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest) from 26.0.4 to 26.0.5. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest) Signed-off-by: dependabot-preview[bot] --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index be1c67176b..536322be17 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "matters-web", - "version": "3.10.1", + "version": "3.10.2", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -6008,9 +6008,9 @@ } }, "@types/jest": { - "version": "26.0.4", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.4.tgz", - "integrity": "sha512-4fQNItvelbNA9+sFgU+fhJo8ZFF+AS4Egk3GWwCW2jFtViukXbnztccafAdLhzE/0EiCogljtQQXP8aQ9J7sFg==", + "version": "26.0.5", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.5.tgz", + "integrity": "sha512-heU+7w8snfwfjtcj2H458aTx3m5unIToOJhx75ebHilBiiQ39OIdA18WkG4LP08YKeAoWAGvWg8s+22w/PeJ6w==", "dev": true, "requires": { "jest-diff": "^25.2.1", diff --git a/package.json b/package.json index 5618911ece..466e2fe104 100644 --- a/package.json +++ b/package.json @@ -98,7 +98,7 @@ "@types/fingerprintjs2": "^2.0.0", "@types/grecaptcha": "^3.0.1", "@types/helmet": "0.0.47", - "@types/jest": "^26.0.4", + "@types/jest": "^26.0.5", "@types/jump.js": "^1.0.3", "@types/lodash": "^4.14.157", "@types/nprogress": "0.2.0", From 755db98c78baca1cc72d2a0cff3eb31580da4960 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 20 Jul 2020 01:52:58 +0000 Subject: [PATCH 66/86] build(deps-dev): bump apollo from 2.30.0 to 2.30.1 Bumps [apollo](https://github.com/apollographql/apollo-tooling) from 2.30.0 to 2.30.1. - [Release notes](https://github.com/apollographql/apollo-tooling/releases) - [Changelog](https://github.com/apollographql/apollo-tooling/blob/master/CHANGELOG.md) - [Commits](https://github.com/apollographql/apollo-tooling/compare/apollo@2.30.0...apollo@2.30.1) Signed-off-by: dependabot-preview[bot] --- package-lock.json | 54 +++++++++++++++++++++++------------------------ package.json | 2 +- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/package-lock.json b/package-lock.json index 789ec0a587..3249113dce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6695,9 +6695,9 @@ } }, "apollo": { - "version": "2.30.0", - "resolved": "https://registry.npmjs.org/apollo/-/apollo-2.30.0.tgz", - "integrity": "sha512-lavRq0n2wmpKZpoW0p4nclzKFDuFJfV9MaC12rb0HOV2lMMBxVt2MJj2YLZzlsTIyZiK34Q5pYFx17plIqKQyA==", + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/apollo/-/apollo-2.30.1.tgz", + "integrity": "sha512-poh2tja2U8U5bGMxfPQjQP1voz2ZaNm/attwC8zpobdeLoT43LeQfFSTqCVmCMZJAfbMa40Cb54yV66DP2w9fw==", "dev": true, "requires": { "@apollographql/apollo-tools": "^0.4.8", @@ -6709,11 +6709,11 @@ "@oclif/plugin-not-found": "1.2.4", "@oclif/plugin-plugins": "1.9.0", "@oclif/plugin-warn-if-update-available": "1.7.0", - "apollo-codegen-core": "^0.37.6", - "apollo-codegen-flow": "^0.35.6", - "apollo-codegen-scala": "^0.36.6", - "apollo-codegen-swift": "^0.37.6", - "apollo-codegen-typescript": "^0.37.6", + "apollo-codegen-core": "^0.37.7", + "apollo-codegen-flow": "^0.35.7", + "apollo-codegen-scala": "^0.36.7", + "apollo-codegen-swift": "^0.37.7", + "apollo-codegen-typescript": "^0.37.7", "apollo-env": "^0.6.5", "apollo-graphql": "^0.5.0", "apollo-language-server": "^1.23.2", @@ -6820,9 +6820,9 @@ } }, "apollo-codegen-core": { - "version": "0.37.6", - "resolved": "https://registry.npmjs.org/apollo-codegen-core/-/apollo-codegen-core-0.37.6.tgz", - "integrity": "sha512-n379jZas7Gwc3RdKHyp0oqAWAuBTWY2sbyqItkDq/n4qorbWpl7veLYwiLtnyIDBNghDbgnv5tDD9ukFy7459A==", + "version": "0.37.7", + "resolved": "https://registry.npmjs.org/apollo-codegen-core/-/apollo-codegen-core-0.37.7.tgz", + "integrity": "sha512-7AMnzS+X7z91eUSctc0mQoQzVJrrKo+zLXevMDkGyTH+q541dYfpAdKQ5nffPcb1ZwwOONZCyl8kc8faJzD0Kw==", "dev": true, "requires": { "@babel/generator": "7.10.4", @@ -6873,14 +6873,14 @@ } }, "apollo-codegen-flow": { - "version": "0.35.6", - "resolved": "https://registry.npmjs.org/apollo-codegen-flow/-/apollo-codegen-flow-0.35.6.tgz", - "integrity": "sha512-alPIGkbhhTq97Leyx5RaneMEK3S2Ol2GghkH0ztEQRbglGx5Pibh0RZZNdDbOoYg/26umTlR1wQGPrJ9325fgw==", + "version": "0.35.7", + "resolved": "https://registry.npmjs.org/apollo-codegen-flow/-/apollo-codegen-flow-0.35.7.tgz", + "integrity": "sha512-q7GsbHE0UtqXFat8wGyidUJRdGkbtfUqCtuQkV5qKOOnudFR32G7dz+6i/Z9R5IqOqWVMpxLq7UeiYRiz8c1dg==", "dev": true, "requires": { "@babel/generator": "7.10.4", "@babel/types": "7.10.4", - "apollo-codegen-core": "^0.37.6", + "apollo-codegen-core": "^0.37.7", "change-case": "^4.0.0", "common-tags": "^1.5.1", "inflected": "^2.0.3" @@ -6924,38 +6924,38 @@ } }, "apollo-codegen-scala": { - "version": "0.36.6", - "resolved": "https://registry.npmjs.org/apollo-codegen-scala/-/apollo-codegen-scala-0.36.6.tgz", - "integrity": "sha512-v83JJEy62w20jTa5RfPYtPbLMi5RmrGpebPMBkiZoSn1MY80KvMoB9sevOsHc5OAb1N5PuKxHlJgD8JKnxeNeQ==", + "version": "0.36.7", + "resolved": "https://registry.npmjs.org/apollo-codegen-scala/-/apollo-codegen-scala-0.36.7.tgz", + "integrity": "sha512-x8EWMOrW4e/kl5QFUHCJsJzemPk1Fa0hMCyjgnlBGQHBrAkHzc33qMMs6WTGvLLL8x8sMvqxCX+NiE/jgtYEvg==", "dev": true, "requires": { - "apollo-codegen-core": "^0.37.6", + "apollo-codegen-core": "^0.37.7", "change-case": "^4.0.0", "common-tags": "^1.5.1", "inflected": "^2.0.3" } }, "apollo-codegen-swift": { - "version": "0.37.6", - "resolved": "https://registry.npmjs.org/apollo-codegen-swift/-/apollo-codegen-swift-0.37.6.tgz", - "integrity": "sha512-45Vjh1blPpKSyknK8tRpJ9Scrh/SYfTi9qqZkT4T51iyRBbQFyiDDidrdaTepU609UwAInt0Q2hwqMZCWoT6ng==", + "version": "0.37.7", + "resolved": "https://registry.npmjs.org/apollo-codegen-swift/-/apollo-codegen-swift-0.37.7.tgz", + "integrity": "sha512-97uCfBt3UVq0hlAWIBZpQoZjgdeKGObxsNp2L2R5ldMLoD3cQzjzuUDJGG1DoAsn5RMqv2gGNEk5QZMrWhidLw==", "dev": true, "requires": { - "apollo-codegen-core": "^0.37.6", + "apollo-codegen-core": "^0.37.7", "change-case": "^4.0.0", "common-tags": "^1.5.1", "inflected": "^2.0.3" } }, "apollo-codegen-typescript": { - "version": "0.37.6", - "resolved": "https://registry.npmjs.org/apollo-codegen-typescript/-/apollo-codegen-typescript-0.37.6.tgz", - "integrity": "sha512-GhcPDNJgsCcVvrO4qKedJqGl/ALyRkTpc5KDQ2597SuQZDAmwVe5JpDePCU1HUsaqt1AsCiBSHu29AQCJFgxgg==", + "version": "0.37.7", + "resolved": "https://registry.npmjs.org/apollo-codegen-typescript/-/apollo-codegen-typescript-0.37.7.tgz", + "integrity": "sha512-LIx1tsWqRrhTcYcRPjhbzBwSaCbMK3UKSN+AlOzNDvG/Rm6wFutHznj14kn/iqcIHmCbGGuFNjiZNbLwCJ3SyQ==", "dev": true, "requires": { "@babel/generator": "7.10.4", "@babel/types": "7.10.4", - "apollo-codegen-core": "^0.37.6", + "apollo-codegen-core": "^0.37.7", "change-case": "^4.0.0", "common-tags": "^1.5.1", "inflected": "^2.0.3" diff --git a/package.json b/package.json index 90003f7f43..18d1c54a0c 100644 --- a/package.json +++ b/package.json @@ -113,7 +113,7 @@ "@types/styled-jsx": "^2.2.8", "@types/validator": "^13.1.0", "@zeit/next-bundle-analyzer": "^0.1.2", - "apollo": "^2.30.0", + "apollo": "^2.30.1", "babel-jest": "^26.1.0", "babel-plugin-dynamic-import-node": "^2.3.3", "babel-polyfill": "^6.26.0", From 17976402b409c7151cbf6d312cd9c83393564fa6 Mon Sep 17 00:00:00 2001 From: RoberTu Date: Mon, 20 Jul 2020 13:19:33 +0800 Subject: [PATCH 67/86] fix(public-private-queries): fix query switching of home feeds --- src/views/Home/Feed/index.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/views/Home/Feed/index.tsx b/src/views/Home/Feed/index.tsx index 21a0e5bf71..1a7c75aa40 100644 --- a/src/views/Home/Feed/index.tsx +++ b/src/views/Home/Feed/index.tsx @@ -88,9 +88,11 @@ const MainFeed = ({ feedSortType: sortBy, viewMode }: MainFeedProps) => { /** * Data Fetching */ + let query = FEED_ARTICLES_PUBLIC[sortBy] + // split out group b if in hottest feed and user is logged in if (isHottestFeed && (!viewer.id || viewer.info.group !== 'b')) { - FEED_ARTICLES_PUBLIC.hottest = FEED_ARTICLES_PUBLIC.valued + query = FEED_ARTICLES_PUBLIC.valued } // public data @@ -102,7 +104,7 @@ const MainFeed = ({ feedSortType: sortBy, viewMode }: MainFeedProps) => { networkStatus, refetch: refetchPublic, client, - } = useQuery(FEED_ARTICLES_PUBLIC[sortBy], { + } = useQuery(query, { notifyOnNetworkStatusChange: true, }) @@ -177,6 +179,10 @@ const MainFeed = ({ feedSortType: sortBy, viewMode }: MainFeedProps) => { * Render */ if (loading && (!result || isNewLoading)) { + if (process.browser) { + window.scrollTo(0, 0) + document.body.focus() + } return } From 2dbd6d7dc9abcf725d9bbe69a14317a5e48701af Mon Sep 17 00:00:00 2001 From: Zeck Li Date: Mon, 20 Jul 2020 14:52:22 +0800 Subject: [PATCH 68/86] fix(component): fix passing wrong arguments to article search API --- .../TagDetail/Buttons/AddButton/TagArticleDialog/Content.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/TagDetail/Buttons/AddButton/TagArticleDialog/Content.tsx b/src/views/TagDetail/Buttons/AddButton/TagArticleDialog/Content.tsx index d850b71752..24c5f41bbd 100644 --- a/src/views/TagDetail/Buttons/AddButton/TagArticleDialog/Content.tsx +++ b/src/views/TagDetail/Buttons/AddButton/TagArticleDialog/Content.tsx @@ -201,7 +201,7 @@ const TagArticleDialogContent: React.FC = ({ dropdownCallback={onClickMenuItem} DropdownContent={DropdownContent} query={SEARCH_ARTICLES} - queryFilter={{ authorId: viewer.id }} + queryFilter={forSelected ? undefined : { authorId: viewer.id }} />
    From 6bbfd01a0ab4f90410bee1c614559a6cc399cb97 Mon Sep 17 00:00:00 2001 From: RoberTu Date: Mon, 20 Jul 2020 15:43:45 +0800 Subject: [PATCH 69/86] fix(public-private-queries): fix viewer articles feed --- .../DropdownActions/StickyButton.tsx | 6 +- src/components/GQL/queries/userArticles.ts | 74 ++++++++++++------- src/views/ArticleDetail/index.tsx | 2 +- .../DraftDetail/PublishState/RetryButton.tsx | 2 +- src/views/User/Articles/UserArticles.tsx | 11 ++- 5 files changed, 58 insertions(+), 37 deletions(-) diff --git a/src/components/ArticleDigest/DropdownActions/StickyButton.tsx b/src/components/ArticleDigest/DropdownActions/StickyButton.tsx index aa0fa6eb7f..456390345f 100644 --- a/src/components/ArticleDigest/DropdownActions/StickyButton.tsx +++ b/src/components/ArticleDigest/DropdownActions/StickyButton.tsx @@ -56,11 +56,7 @@ const StickyButton = ({ article }: { article: StickyButtonArticle }) => { }) return ( - { - toggleSticky() - }} - > + {article.sticky ? ( } size="md" spacing="base"> diff --git a/src/components/GQL/queries/userArticles.ts b/src/components/GQL/queries/userArticles.ts index 0f5974dcd7..cd5cabb6ba 100644 --- a/src/components/GQL/queries/userArticles.ts +++ b/src/components/GQL/queries/userArticles.ts @@ -2,43 +2,61 @@ import gql from 'graphql-tag' import { ArticleDigestFeed } from '~/components' -export const USER_ARTICLES_PUBLIC = gql` - query UserArticlesPublic($userName: String!, $after: String) { - user(input: { userName: $userName }) { - id - displayName - info { - description - profileCover +const fragment = gql` + fragment ArticlesUser on User { + id + displayName + info { + description + profileCover + } + articles(input: { first: 10, after: $after }) { + totalCount + pageInfo { + startCursor + endCursor + hasNextPage } - articles(input: { first: 10, after: $after }) { - totalCount - pageInfo { - startCursor - endCursor - hasNextPage + edges { + cursor + node { + createdAt + wordCount + ...ArticleDigestFeedArticlePublic + ...ArticleDigestFeedArticlePrivate } - edges { - cursor - node { - createdAt - wordCount - ...ArticleDigestFeedArticlePublic - ...ArticleDigestFeedArticlePrivate - } - } - } - status { - state - articleCount - totalWordCount } } + status { + state + articleCount + totalWordCount + } } ${ArticleDigestFeed.fragments.article.public} ${ArticleDigestFeed.fragments.article.private} ` +// without `Public` suffix, query as a logged-in user +export const VIEWER_ARTICLES = gql` + query ViewerArticles($userName: String!, $after: String) { + user(input: { userName: $userName }) @connection(key: "viewerArticles") { + ...ArticlesUser + } + } + ${fragment} +` + +// with `Public` suffix, query as an anonymous user +export const USER_ARTICLES_PUBLIC = gql` + query UserArticlesPublic($userName: String!, $after: String) { + user(input: { userName: $userName }) { + ...ArticlesUser + } + } + ${fragment} +` + export const USER_ARTICLES_PRIVATE = gql` query UserArticlesPrivate($ids: [ID!]!) { nodes(input: { ids: $ids }) { diff --git a/src/views/ArticleDetail/index.tsx b/src/views/ArticleDetail/index.tsx index 0d48bd227e..42d96d44fd 100644 --- a/src/views/ArticleDetail/index.tsx +++ b/src/views/ArticleDetail/index.tsx @@ -116,7 +116,7 @@ const ArticleDetail = () => { useEffect(() => { loadPrivate() - }, [mediaHash, viewer.id, article]) + }, [mediaHash, !!article, viewer.id]) // translation const [translate, setTranslate] = useState(false) diff --git a/src/views/Me/DraftDetail/PublishState/RetryButton.tsx b/src/views/Me/DraftDetail/PublishState/RetryButton.tsx index aed19919e1..65ba2da319 100644 --- a/src/views/Me/DraftDetail/PublishState/RetryButton.tsx +++ b/src/views/Me/DraftDetail/PublishState/RetryButton.tsx @@ -33,7 +33,7 @@ const RetryButton = ({ id }: { id: string }) => { size={[null, '1.25rem']} spacing={[0, 'xtight']} bgActiveColor="red" - onClick={() => retry()} + onClick={retry} > { const viewer = useContext(ViewerContext) const router = useRouter() const userName = getQuery({ router, key: 'userName' }) + const isViewer = viewer.userName === userName + + let query = USER_ARTICLES_PUBLIC + if (isViewer) { + query = VIEWER_ARTICLES + } /** * Data Fetching @@ -71,7 +78,7 @@ const UserArticles = () => { fetchMore, refetch: refetchPublic, client, - } = useQuery(USER_ARTICLES_PUBLIC, { + } = useQuery(query, { variables: { userName }, }) @@ -82,7 +89,7 @@ const UserArticles = () => { // private data const loadPrivate = (publicData?: UserArticlesPublic) => { - if (!viewer.id || !publicData || !user) { + if (!viewer.id || isViewer || !publicData || !user) { return } From 0068f3bd50df54786c3e12bdd800f312e83c37ee Mon Sep 17 00:00:00 2001 From: Zeck Li Date: Mon, 20 Jul 2020 17:16:20 +0800 Subject: [PATCH 70/86] fix(component): revise the count description of followers --- src/views/TagDetail/Followers/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/TagDetail/Followers/index.tsx b/src/views/TagDetail/Followers/index.tsx index 39c28006b3..77f3bacc59 100644 --- a/src/views/TagDetail/Followers/index.tsx +++ b/src/views/TagDetail/Followers/index.tsx @@ -46,7 +46,7 @@ const Followers = ({ id }: FollowersProps) => {
    {numAbbr(totalCount)} - +
    From a8c34c45935e6f23316f3da9503bea2cddbdda47 Mon Sep 17 00:00:00 2001 From: RoberTu Date: Tue, 21 Jul 2020 10:10:30 +0800 Subject: [PATCH 71/86] fix(public-private-queries): show spinner until private viewer fetched --- src/views/Follow/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/Follow/index.tsx b/src/views/Follow/index.tsx index d8ba2d5b39..3400928269 100644 --- a/src/views/Follow/index.tsx +++ b/src/views/Follow/index.tsx @@ -45,7 +45,7 @@ const BaseFollow = () => { } }, []) - if (loading) { + if (loading || !viewer.privateFetched) { return } From 5d2091b35e512d7c13a13ee990b6c8d72e8f5442 Mon Sep 17 00:00:00 2001 From: RoberTu Date: Tue, 21 Jul 2020 11:26:35 +0800 Subject: [PATCH 72/86] feat(public-private-queries): search pages --- src/components/Search/SearchOverview/gql.ts | 24 ++++ .../Search/SearchOverview/index.tsx | 49 +++++--- .../Search/AggregateResults/Articles.tsx | 50 ++++---- src/views/Search/AggregateResults/Tags.tsx | 44 +++---- src/views/Search/AggregateResults/Users.tsx | 44 +++---- src/views/Search/AggregateResults/gql.ts | 66 ++++++++++ src/views/Search/AggregateResults/index.tsx | 4 + src/views/Search/SearchArticles/gql.ts | 38 ++++++ src/views/Search/SearchArticles/index.tsx | 114 +++++++++++------- src/views/Search/SearchTags/gql.ts | 24 ++++ src/views/Search/SearchTags/index.tsx | 67 +++++----- src/views/Search/SearchUsers/gql.ts | 38 ++++++ src/views/Search/SearchUsers/index.tsx | 112 ++++++++++------- 13 files changed, 448 insertions(+), 226 deletions(-) create mode 100644 src/components/Search/SearchOverview/gql.ts create mode 100644 src/views/Search/AggregateResults/gql.ts create mode 100644 src/views/Search/SearchArticles/gql.ts create mode 100644 src/views/Search/SearchTags/gql.ts create mode 100644 src/views/Search/SearchUsers/gql.ts diff --git a/src/components/Search/SearchOverview/gql.ts b/src/components/Search/SearchOverview/gql.ts new file mode 100644 index 0000000000..f4c44491e5 --- /dev/null +++ b/src/components/Search/SearchOverview/gql.ts @@ -0,0 +1,24 @@ +import gql from 'graphql-tag' + +import ClearHistoryButton from './ClearHistoryButton' + +export const SEARCH_AUTOCOMPLETE_PUBLIC = gql` + query SearchOverviewPublic { + frequentSearch(input: { first: 5, key: "" }) + viewer { + id + ...RecentSearchesUser + } + } + ${ClearHistoryButton.fragments.user} +` + +export const SEARCH_AUTOCOMPLETE_PRIVATE = gql` + query SearchOverviewPrivate { + viewer { + id + ...RecentSearchesUser + } + } + ${ClearHistoryButton.fragments.user} +` diff --git a/src/components/Search/SearchOverview/index.tsx b/src/components/Search/SearchOverview/index.tsx index 7e4ca991af..7c2a45fb0e 100644 --- a/src/components/Search/SearchOverview/index.tsx +++ b/src/components/Search/SearchOverview/index.tsx @@ -1,36 +1,33 @@ import { useQuery } from '@apollo/react-hooks' import classNames from 'classnames' -import gql from 'graphql-tag' import Link from 'next/link' -import { Fragment } from 'react' +import { Fragment, useContext, useEffect } from 'react' -import { Menu, Translate } from '~/components' +import { Menu, Translate, ViewerContext } from '~/components' import { Spinner } from '~/components/Spinner' import { toPath } from '~/common/utils' import ClearHistoryButton from './ClearHistoryButton' +import { SEARCH_AUTOCOMPLETE_PRIVATE, SEARCH_AUTOCOMPLETE_PUBLIC } from './gql' import styles from './styles.css' -import { SearchOverview as SearchOverviewType } from './__generated__/SearchOverview' +import { SearchOverviewPublic } from './__generated__/SearchOverviewPublic' interface SearchOverviewProps { inPage?: boolean } -const SEARCH_AUTOCOMPLETE = gql` - query SearchOverview { - frequentSearch(input: { first: 5, key: "" }) - viewer { - id - ...RecentSearchesUser - } - } - ${ClearHistoryButton.fragments.user} -` - export const SearchOverview = ({ inPage }: SearchOverviewProps) => { - const { data, loading } = useQuery(SEARCH_AUTOCOMPLETE) + const viewer = useContext(ViewerContext) + + /** + * Data Fetching + */ + // public data + const { data, loading, client } = useQuery( + SEARCH_AUTOCOMPLETE_PUBLIC + ) const frequentSearch = data?.frequentSearch || [] const recentSearches = data?.viewer?.activity.recentSearches.edges || [] @@ -46,6 +43,25 @@ export const SearchOverview = ({ inPage }: SearchOverviewProps) => { inPage, }) + // private data + const loadPrivate = () => { + if (!viewer.id) { + return + } + + client.query({ + query: SEARCH_AUTOCOMPLETE_PRIVATE, + fetchPolicy: 'network-only', + }) + } + + useEffect(() => { + loadPrivate() + }, [viewer.id]) + + /** + * Render + */ if (loading) { return ( @@ -55,7 +71,6 @@ export const SearchOverview = ({ inPage }: SearchOverviewProps) => { } if (!showFrequentSearch && !showSearchHistory) { - // TODO: Empty Notice return null } diff --git a/src/views/Search/AggregateResults/Articles.tsx b/src/views/Search/AggregateResults/Articles.tsx index c5ebadb0f1..a0266727c2 100644 --- a/src/views/Search/AggregateResults/Articles.tsx +++ b/src/views/Search/AggregateResults/Articles.tsx @@ -1,52 +1,46 @@ import { useQuery } from '@apollo/react-hooks' -import gql from 'graphql-tag' import { useRouter } from 'next/router' -import { ArticleDigestTitle, Card, List, Spinner } from '~/components' +import { + ArticleDigestTitle, + Card, + List, + Spinner, + usePullToRefresh, +} from '~/components' import { analytics, getQuery, toPath } from '~/common/utils' +import { SEARCH_AGGREGATE_ARTICLES_PUBLIC } from './gql' import styles from './styles.css' import ViewMoreButton from './ViewMoreButton' -import { SeachAggregateArticles } from './__generated__/SeachAggregateArticles' - -const SEARCH_AGGREGATE_ARTICLES = gql` - query SeachAggregateArticles($key: String!) { - search(input: { key: $key, type: Article, first: 4 }) { - pageInfo { - startCursor - endCursor - hasNextPage - } - edges { - cursor - node { - ... on Article { - ...ArticleDigestTitleArticle - } - } - } - } - } - ${ArticleDigestTitle.fragments.article} -` +import { SeachAggregateArticlesPublic } from './__generated__/SeachAggregateArticlesPublic' const AggregateArticleResults = () => { const router = useRouter() const q = getQuery({ router, key: 'q' }) - const { data, loading } = useQuery( - SEARCH_AGGREGATE_ARTICLES, + /** + * Data Fetching + */ + // public data + const { data, loading, refetch } = useQuery( + SEARCH_AGGREGATE_ARTICLES_PUBLIC, { variables: { key: q } } ) + const { edges, pageInfo } = data?.search || {} + + usePullToRefresh.Handler(refetch) + + /** + * Render + */ if (loading) { return } - const { edges, pageInfo } = data?.search || {} - if (!edges || edges.length <= 0 || !pageInfo) { return null } diff --git a/src/views/Search/AggregateResults/Tags.tsx b/src/views/Search/AggregateResults/Tags.tsx index d9adad25b5..e29cd8d3c0 100644 --- a/src/views/Search/AggregateResults/Tags.tsx +++ b/src/views/Search/AggregateResults/Tags.tsx @@ -1,52 +1,40 @@ import { useQuery } from '@apollo/react-hooks' -import gql from 'graphql-tag' import { useRouter } from 'next/router' -import { Card, List, Spinner, Tag } from '~/components' +import { Card, List, Spinner, Tag, usePullToRefresh } from '~/components' import { analytics, getQuery, toPath } from '~/common/utils' +import { SEARCH_AGGREGATE_TAGS_PUBLIC } from './gql' import styles from './styles.css' import ViewMoreButton from './ViewMoreButton' -import { SeachAggregateTags } from './__generated__/SeachAggregateTags' - -const SEARCH_AGGREGATE_TAGS = gql` - query SeachAggregateTags($key: String!) { - search(input: { key: $key, type: Tag, first: 3 }) { - pageInfo { - startCursor - endCursor - hasNextPage - } - edges { - cursor - node { - ... on Tag { - ...DigestTag - } - } - } - } - } - ${Tag.fragments.tag} -` +import { SeachAggregateTagsPublic } from './__generated__/SeachAggregateTagsPublic' const AggregateTagResults = () => { const router = useRouter() const q = getQuery({ router, key: 'q' }) - const { data, loading } = useQuery( - SEARCH_AGGREGATE_TAGS, + /** + * Data Fetching + */ + // public data + const { data, loading, refetch } = useQuery( + SEARCH_AGGREGATE_TAGS_PUBLIC, { variables: { key: q } } ) + const { edges, pageInfo } = data?.search || {} + + usePullToRefresh.Handler(refetch) + + /** + * Render + */ if (loading) { return } - const { edges, pageInfo } = data?.search || {} - if (!edges || edges.length <= 0 || !pageInfo) { return null } diff --git a/src/views/Search/AggregateResults/Users.tsx b/src/views/Search/AggregateResults/Users.tsx index 552ed8b4b0..63595c4507 100644 --- a/src/views/Search/AggregateResults/Users.tsx +++ b/src/views/Search/AggregateResults/Users.tsx @@ -1,52 +1,40 @@ import { useQuery } from '@apollo/react-hooks' -import gql from 'graphql-tag' import { useRouter } from 'next/router' -import { Card, List, Spinner, UserDigest } from '~/components' +import { Card, List, Spinner, usePullToRefresh, UserDigest } from '~/components' import { analytics, getQuery, toPath } from '~/common/utils' +import { SEARCH_AGGREGATE_USERS_PUBLIC } from './gql' import styles from './styles.css' import ViewMoreButton from './ViewMoreButton' -import { SeachAggregateUsers } from './__generated__/SeachAggregateUsers' - -const SEARCH_AGGREGATE_USERS = gql` - query SeachAggregateUsers($key: String!) { - search(input: { key: $key, type: User, first: 3 }) { - pageInfo { - startCursor - endCursor - hasNextPage - } - edges { - cursor - node { - ... on User { - ...UserDigestMiniUser - } - } - } - } - } - ${UserDigest.Mini.fragments.user} -` +import { SeachAggregateUsersPublic } from './__generated__/SeachAggregateUsersPublic' const AggregateUserResults = () => { const router = useRouter() const q = getQuery({ router, key: 'q' }) - const { data, loading } = useQuery( - SEARCH_AGGREGATE_USERS, + /** + * Data Fetching + */ + // public data + const { data, loading, refetch } = useQuery( + SEARCH_AGGREGATE_USERS_PUBLIC, { variables: { key: q } } ) + const { edges, pageInfo } = data?.search || {} + + usePullToRefresh.Handler(refetch) + + /** + * Render + */ if (loading) { return } - const { edges, pageInfo } = data?.search || {} - if (!edges || edges.length <= 0 || !pageInfo) { return null } diff --git a/src/views/Search/AggregateResults/gql.ts b/src/views/Search/AggregateResults/gql.ts new file mode 100644 index 0000000000..b236f6c521 --- /dev/null +++ b/src/views/Search/AggregateResults/gql.ts @@ -0,0 +1,66 @@ +import gql from 'graphql-tag' + +import { ArticleDigestTitle, Tag, UserDigest } from '~/components' + +export const SEARCH_AGGREGATE_ARTICLES_PUBLIC = gql` + query SeachAggregateArticlesPublic($key: String!) { + search(input: { key: $key, type: Article, first: 4 }) { + pageInfo { + startCursor + endCursor + hasNextPage + } + edges { + cursor + node { + ... on Article { + ...ArticleDigestTitleArticle + } + } + } + } + } + ${ArticleDigestTitle.fragments.article} +` + +export const SEARCH_AGGREGATE_TAGS_PUBLIC = gql` + query SeachAggregateTagsPublic($key: String!) { + search(input: { key: $key, type: Tag, first: 3 }) { + pageInfo { + startCursor + endCursor + hasNextPage + } + edges { + cursor + node { + ... on Tag { + ...DigestTag + } + } + } + } + } + ${Tag.fragments.tag} +` + +export const SEARCH_AGGREGATE_USERS_PUBLIC = gql` + query SeachAggregateUsersPublic($key: String!) { + search(input: { key: $key, type: User, first: 3 }) { + pageInfo { + startCursor + endCursor + hasNextPage + } + edges { + cursor + node { + ... on User { + ...UserDigestMiniUser + } + } + } + } + } + ${UserDigest.Mini.fragments.user} +` diff --git a/src/views/Search/AggregateResults/index.tsx b/src/views/Search/AggregateResults/index.tsx index 781d3e5071..cbde900a97 100644 --- a/src/views/Search/AggregateResults/index.tsx +++ b/src/views/Search/AggregateResults/index.tsx @@ -1,8 +1,12 @@ +import { usePullToRefresh } from '~/components' + import Articles from './Articles' import Tags from './Tags' import Users from './Users' const AggregateResults = () => { + usePullToRefresh.Register() + return ( <> diff --git a/src/views/Search/SearchArticles/gql.ts b/src/views/Search/SearchArticles/gql.ts new file mode 100644 index 0000000000..615228118b --- /dev/null +++ b/src/views/Search/SearchArticles/gql.ts @@ -0,0 +1,38 @@ +import gql from 'graphql-tag' + +import { ArticleDigestFeed } from '~/components' + +export const SEARCH_ARTICLES_PUBLIC = gql` + query SeachArticlesPublic($key: String!, $first: Int!, $after: String) { + search(input: { key: $key, type: Article, first: $first, after: $after }) { + pageInfo { + startCursor + endCursor + hasNextPage + } + edges { + cursor + node { + ... on Article { + ...ArticleDigestFeedArticlePublic + ...ArticleDigestFeedArticlePrivate + } + } + } + } + } + ${ArticleDigestFeed.fragments.article.public} + ${ArticleDigestFeed.fragments.article.private} +` + +export const SEARCH_ARTICLES_PRIVATE = gql` + query SeachArticlesPrivate($ids: [ID!]!) { + nodes(input: { ids: $ids }) { + id + ... on Article { + ...ArticleDigestFeedArticlePrivate + } + } + } + ${ArticleDigestFeed.fragments.article.private} +` diff --git a/src/views/Search/SearchArticles/index.tsx b/src/views/Search/SearchArticles/index.tsx index 7192b2f329..c6c304ee84 100644 --- a/src/views/Search/SearchArticles/index.tsx +++ b/src/views/Search/SearchArticles/index.tsx @@ -1,7 +1,7 @@ import { useQuery } from '@apollo/react-hooks' import { NetworkStatus } from 'apollo-client' -import gql from 'graphql-tag' import { useRouter } from 'next/router' +import { useContext, useEffect } from 'react' import { ArticleDigestFeed, @@ -9,71 +9,78 @@ import { List, Spinner, Translate, - usePullToRefresh, + ViewerContext, } from '~/components' import { analytics, getQuery, mergeConnections } from '~/common/utils' import EmptySearch from '../EmptySearch' +import { SEARCH_ARTICLES_PRIVATE, SEARCH_ARTICLES_PUBLIC } from './gql' -import { SeachArticles } from './__generated__/SeachArticles' - -const SEARCH_ARTICLES = gql` - query SeachArticles($key: String!, $first: Int!, $after: String) { - search(input: { key: $key, type: Article, first: $first, after: $after }) { - pageInfo { - startCursor - endCursor - hasNextPage - } - edges { - cursor - node { - ... on Article { - ...ArticleDigestFeedArticlePublic - ...ArticleDigestFeedArticlePrivate - } - } - } - } - } - ${ArticleDigestFeed.fragments.article.public} - ${ArticleDigestFeed.fragments.article.private} -` +import { SeachArticlesPublic } from './__generated__/SeachArticlesPublic' const SearchArticles = () => { + const viewer = useContext(ViewerContext) const router = useRouter() const q = getQuery({ router, key: 'q' }) - const { data, loading, fetchMore, networkStatus, refetch } = useQuery< - SeachArticles - >(SEARCH_ARTICLES, { + /** + * Data Fetching + */ + // public data + const { + data, + loading, + fetchMore, + networkStatus, + refetch: refetchPublic, + client, + } = useQuery(SEARCH_ARTICLES_PUBLIC, { variables: { key: q, first: 10 }, notifyOnNetworkStatusChange: true, }) const isNewLoading = networkStatus === NetworkStatus.setVariables - usePullToRefresh.Handler(refetch) - - if (loading && (!data?.search || isNewLoading)) { - return - } - + // pagination const connectionPath = 'search' const { edges, pageInfo } = data?.search || {} - if (!edges || edges.length <= 0 || !pageInfo) { - return } /> + // private data + const loadPrivate = (publicData?: SeachArticlesPublic) => { + if (!viewer.id || !publicData) { + return + } + + const publicIds = (publicData?.search.edges || []) + .filter(({ node }) => node.__typename === 'Article') + .map(({ node }) => node.__typename === 'Article' && node.id) + + client.query({ + query: SEARCH_ARTICLES_PRIVATE, + fetchPolicy: 'network-only', + variables: { ids: publicIds }, + }) } - const loadMore = () => { + // fetch private data for first page + useEffect(() => { + if (loading || !edges) { + return + } + + loadPrivate(data) + }, [!!edges, viewer.id]) + + // load next page + const loadMore = async () => { analytics.trackEvent('load_more', { type: 'search_article', - location: edges.length, + location: edges?.length || 0, }) - return fetchMore({ + + const { data: newData } = await fetchMore({ variables: { - after: pageInfo.endCursor, + after: pageInfo?.endCursor, }, updateQuery: (previousResult, { fetchMoreResult }) => mergeConnections({ @@ -82,10 +89,33 @@ const SearchArticles = () => { path: connectionPath, }), }) + + loadPrivate(newData) + } + + // refetch & pull to refresh + const refetch = async () => { + const { data: newData } = await refetchPublic() + loadPrivate(newData) + } + + /** + * Render + */ + if (loading && (!data?.search || isNewLoading)) { + return + } + + if (!edges || edges.length <= 0 || !pageInfo) { + return } /> } return ( - + {edges.map( ({ node, cursor }, i) => diff --git a/src/views/Search/SearchTags/gql.ts b/src/views/Search/SearchTags/gql.ts new file mode 100644 index 0000000000..8f6ec02b57 --- /dev/null +++ b/src/views/Search/SearchTags/gql.ts @@ -0,0 +1,24 @@ +import gql from 'graphql-tag' + +import { Tag } from '~/components' + +export const SEARCH_TAGS_PUBLIC = gql` + query SeachTagsPublic($key: String!, $after: String) { + search(input: { key: $key, type: Tag, first: 20, after: $after }) { + pageInfo { + startCursor + endCursor + hasNextPage + } + edges { + cursor + node { + ... on Tag { + ...DigestTag + } + } + } + } + } + ${Tag.fragments.tag} +` diff --git a/src/views/Search/SearchTags/index.tsx b/src/views/Search/SearchTags/index.tsx index 85ba662f74..700c9e23ce 100644 --- a/src/views/Search/SearchTags/index.tsx +++ b/src/views/Search/SearchTags/index.tsx @@ -1,5 +1,4 @@ import { useQuery } from '@apollo/react-hooks' -import gql from 'graphql-tag' import { useRouter } from 'next/router' import { @@ -9,68 +8,43 @@ import { Spinner, Tag, Translate, - usePullToRefresh, } from '~/components' import { analytics, getQuery, mergeConnections, toPath } from '~/common/utils' import EmptySearch from '../EmptySearch' +import { SEARCH_TAGS_PUBLIC } from './gql' -import { SeachTags } from './__generated__/SeachTags' - -const SEARCH_TAGS = gql` - query SeachTags($key: String!, $after: String) { - search(input: { key: $key, type: Tag, first: 20, after: $after }) { - pageInfo { - startCursor - endCursor - hasNextPage - } - edges { - cursor - node { - ... on Tag { - ...DigestTag - } - } - } - } - } - ${Tag.fragments.tag} -` +import { SeachTagsPublic } from './__generated__/SeachTagsPublic' const SearchTag = () => { const router = useRouter() const q = getQuery({ router, key: 'q' }) - const { data, loading, fetchMore, refetch } = useQuery( - SEARCH_TAGS, + /** + * Data Fetching + */ + // public data + const { data, loading, fetchMore, refetch } = useQuery( + SEARCH_TAGS_PUBLIC, { variables: { key: q }, } ) - usePullToRefresh.Handler(refetch) - - if (loading) { - return - } - + // pagination const connectionPath = 'search' const { edges, pageInfo } = data?.search || {} - if (!edges || edges.length <= 0 || !pageInfo) { - return null - } - + // load next page const loadMore = () => { analytics.trackEvent('load_more', { type: 'search_tag', - location: edges.length, + location: edges?.length || 0, }) return fetchMore({ variables: { - after: pageInfo.endCursor, + after: pageInfo?.endCursor, }, updateQuery: (previousResult, { fetchMoreResult }) => mergeConnections({ @@ -81,12 +55,27 @@ const SearchTag = () => { }) } + /** + * Render + */ + if (loading) { + return + } + + if (!edges || edges.length <= 0 || !pageInfo) { + return null + } + if (edges.length <= 0) { return } /> } return ( - + {edges.map( ({ node, cursor }, i) => diff --git a/src/views/Search/SearchUsers/gql.ts b/src/views/Search/SearchUsers/gql.ts new file mode 100644 index 0000000000..83bf024d19 --- /dev/null +++ b/src/views/Search/SearchUsers/gql.ts @@ -0,0 +1,38 @@ +import gql from 'graphql-tag' + +import { UserDigest } from '~/components' + +export const SEARCH_USERS_PUBLIC = gql` + query SeachUsersPublic($key: String!, $after: String) { + search(input: { key: $key, type: User, first: 20, after: $after }) { + pageInfo { + startCursor + endCursor + hasNextPage + } + edges { + cursor + node { + ... on User { + ...UserDigestRichUserPublic + ...UserDigestRichUserPrivate + } + } + } + } + } + ${UserDigest.Rich.fragments.user.public} + ${UserDigest.Rich.fragments.user.private} +` + +export const SEARCH_USERS_PRIVATE = gql` + query SeachUsersPrivate($ids: [ID!]!) { + nodes(input: { ids: $ids }) { + id + ... on User { + ...UserDigestRichUserPrivate + } + } + } + ${UserDigest.Rich.fragments.user.private} +` diff --git a/src/views/Search/SearchUsers/index.tsx b/src/views/Search/SearchUsers/index.tsx index 5c06068761..5aab87a95b 100644 --- a/src/views/Search/SearchUsers/index.tsx +++ b/src/views/Search/SearchUsers/index.tsx @@ -1,77 +1,78 @@ import { useQuery } from '@apollo/react-hooks' -import gql from 'graphql-tag' import { useRouter } from 'next/router' +import { useContext, useEffect } from 'react' import { InfiniteScroll, List, Spinner, Translate, - usePullToRefresh, UserDigest, + ViewerContext, } from '~/components' import { analytics, getQuery, mergeConnections } from '~/common/utils' import EmptySearch from '../EmptySearch' +import { SEARCH_USERS_PRIVATE, SEARCH_USERS_PUBLIC } from './gql' -import { SeachUsers } from './__generated__/SeachUsers' - -const SEARCH_USERS = gql` - query SeachUsers($key: String!, $after: String) { - search(input: { key: $key, type: User, first: 20, after: $after }) { - pageInfo { - startCursor - endCursor - hasNextPage - } - edges { - cursor - node { - ... on User { - ...UserDigestRichUserPublic - ...UserDigestRichUserPrivate - } - } - } - } - } - ${UserDigest.Rich.fragments.user.public} - ${UserDigest.Rich.fragments.user.private} -` +import { SeachUsersPublic } from './__generated__/SeachUsersPublic' const SearchUser = () => { + const viewer = useContext(ViewerContext) const router = useRouter() const q = getQuery({ router, key: 'q' }) - const { data, loading, fetchMore, refetch } = useQuery( - SEARCH_USERS, - { - variables: { key: q }, + /** + * Data Fetching + */ + // public data + const { data, loading, fetchMore, refetch: refetchPublic, client } = useQuery< + SeachUsersPublic + >(SEARCH_USERS_PUBLIC, { + variables: { key: q }, + }) + + // pagination + const connectionPath = 'search' + const { edges, pageInfo } = data?.search || {} + + // private data + const loadPrivate = (publicData?: SeachUsersPublic) => { + if (!viewer.id || !publicData) { + return } - ) - usePullToRefresh.Handler(refetch) + const publicIds = (publicData?.search.edges || []) + .filter(({ node }) => node.__typename === 'User') + .map(({ node }) => node.__typename === 'User' && node.id) - if (loading) { - return + client.query({ + query: SEARCH_USERS_PRIVATE, + fetchPolicy: 'network-only', + variables: { ids: publicIds }, + }) } - const connectionPath = 'search' - const { edges, pageInfo } = data?.search || {} + // fetch private data for first page + useEffect(() => { + if (loading || !edges) { + return + } - if (!edges || edges.length <= 0 || !pageInfo) { - return } /> - } + loadPrivate(data) + }, [!!edges, viewer.id]) - const loadMore = () => { + // load next page + const loadMore = async () => { analytics.trackEvent('load_more', { type: 'search_user', - location: edges.length, + location: edges?.length || 0, }) - return fetchMore({ + + const { data: newData } = await fetchMore({ variables: { - after: pageInfo.endCursor, + after: pageInfo?.endCursor, }, updateQuery: (previousResult, { fetchMoreResult }) => mergeConnections({ @@ -80,10 +81,33 @@ const SearchUser = () => { path: connectionPath, }), }) + + loadPrivate(newData) + } + + // refetch & pull to refresh + const refetch = async () => { + const { data: newData } = await refetchPublic() + loadPrivate(newData) + } + + /** + * Render + */ + if (loading) { + return + } + + if (!edges || edges.length <= 0 || !pageInfo) { + return } /> } return ( - + {edges.map( ({ node, cursor }, i) => From 3e43a552a104473a41553114909a45f0404ac2e4 Mon Sep 17 00:00:00 2001 From: RoberTu Date: Tue, 21 Jul 2020 12:33:24 +0800 Subject: [PATCH 73/86] feat(public-private-queries): `` --- src/components/Avatar/styles.css | 1 + .../TagDetail/Buttons/FollowButton/index.tsx | 14 ++-- src/views/TagDetail/gql.ts | 43 ++++++++++ src/views/TagDetail/index.tsx | 80 +++++++++++-------- 4 files changed, 98 insertions(+), 40 deletions(-) create mode 100644 src/views/TagDetail/gql.ts diff --git a/src/components/Avatar/styles.css b/src/components/Avatar/styles.css index cd575d268e..aa91d8dd4c 100644 --- a/src/components/Avatar/styles.css +++ b/src/components/Avatar/styles.css @@ -4,6 +4,7 @@ /* may under a flex layout, keep it unshrinkable */ flex-shrink: 0; + border-radius: 50%; & :global(img) { @mixin object-fit-cover; diff --git a/src/views/TagDetail/Buttons/FollowButton/index.tsx b/src/views/TagDetail/Buttons/FollowButton/index.tsx index 3c6ef884b0..5056fc381c 100644 --- a/src/views/TagDetail/Buttons/FollowButton/index.tsx +++ b/src/views/TagDetail/Buttons/FollowButton/index.tsx @@ -10,12 +10,14 @@ interface FollowButtonProps { } const fragments = { - tag: gql` - fragment FollowButtonTag on Tag { - id - isFollower - } - `, + tag: { + private: gql` + fragment FollowButtonTagPrivate on Tag { + id + isFollower + } + `, + }, } const FollowButton = ({ tag }: FollowButtonProps) => { diff --git a/src/views/TagDetail/gql.ts b/src/views/TagDetail/gql.ts new file mode 100644 index 0000000000..373209e08b --- /dev/null +++ b/src/views/TagDetail/gql.ts @@ -0,0 +1,43 @@ +import gql from 'graphql-tag' + +import { UserDigest } from '~/components/UserDigest' + +import { TagDetailButtons } from './Buttons' + +export const TAG_DETAIL_PUBLIC = gql` + query TagDetailPublic($id: ID!) { + node(input: { id: $id }) { + ... on Tag { + id + content + creator { + id + ...UserDigestMiniUser + } + description + editors { + id + ...UserDigestMiniUser + } + articles(input: { first: 0, selected: true }) { + totalCount + } + ...FollowButtonTagPrivate + } + } + } + ${UserDigest.Mini.fragments.user} + ${TagDetailButtons.FollowButton.fragments.tag.private} +` + +export const TAG_DETAIL_PRIVATE = gql` + query TagDetailPrivate($id: ID!) { + node(input: { id: $id }) { + ... on Tag { + id + ...FollowButtonTagPrivate + } + } + } + ${TagDetailButtons.FollowButton.fragments.tag.private} +` diff --git a/src/views/TagDetail/index.tsx b/src/views/TagDetail/index.tsx index ccbaaed814..5459eb2f79 100644 --- a/src/views/TagDetail/index.tsx +++ b/src/views/TagDetail/index.tsx @@ -1,5 +1,4 @@ import { useQuery } from '@apollo/react-hooks' -import gql from 'graphql-tag' import _find from 'lodash/find' import _some from 'lodash/some' import { useRouter } from 'next/router' @@ -17,6 +16,7 @@ import { Throw404, Title, Translate, + usePullToRefresh, ViewerContext, } from '~/components' import { getErrorCodes, QueryError } from '~/components/GQL' @@ -30,39 +30,14 @@ import ArticlesCount from './ArticlesCount' import { TagDetailButtons } from './Buttons' import DropdownActions from './DropdownActions' import Followers from './Followers' +import { TAG_DETAIL_PRIVATE, TAG_DETAIL_PUBLIC } from './gql' import styles from './styles.css' import { - TagDetail as TagDetailType, - TagDetail_node_Tag, - TagDetail_node_Tag_editors, -} from './__generated__/TagDetail' - -const TAG_DETAIL = gql` - query TagDetail($id: ID!) { - node(input: { id: $id }) { - ... on Tag { - id - content - creator { - id - ...UserDigestMiniUser - } - description - editors { - id - ...UserDigestMiniUser - } - articles(input: { first: 0, selected: true }) { - totalCount - } - ...FollowButtonTag - } - } - } - ${UserDigest.Mini.fragments.user} - ${TagDetailButtons.FollowButton.fragments.tag} -` + TagDetailPublic, + TagDetailPublic_node_Tag, + TagDetailPublic_node_Tag_editors, +} from './__generated__/TagDetailPublic' type TagFeedType = 'latest' | 'selected' @@ -73,7 +48,7 @@ const EmptyLayout: React.FC = ({ children }) => ( ) -const TagDetail = ({ tag }: { tag: TagDetail_node_Tag }) => { +const TagDetail = ({ tag }: { tag: TagDetailPublic_node_Tag }) => { const viewer = useContext(ViewerContext) // feed type @@ -90,7 +65,7 @@ const TagDetail = ({ tag }: { tag: TagDetail_node_Tag }) => { }) // define permission - const filter = ({ displayName }: TagDetail_node_Tag_editors) => + const filter = ({ displayName }: TagDetailPublic_node_Tag_editors) => (displayName || '').toLowerCase() !== 'matty' const editors = tag?.editors || [] const owner = _find(editors, filter) @@ -184,12 +159,49 @@ const TagDetail = ({ tag }: { tag: TagDetail_node_Tag }) => { } const TagDetailContainer = () => { + const viewer = useContext(ViewerContext) const router = useRouter() const tagId = getQuery({ router, key: 'tagId' }) - const { data, loading, error } = useQuery(TAG_DETAIL, { + + /** + * Data Fetching + */ + // public data + const { data, loading, error, refetch: refetchPublic, client } = useQuery< + TagDetailPublic + >(TAG_DETAIL_PUBLIC, { variables: { id: tagId }, }) + // private data + const loadPrivate = () => { + if (!viewer.id || !tagId) { + return + } + + client.query({ + query: TAG_DETAIL_PRIVATE, + fetchPolicy: 'network-only', + variables: { id: tagId }, + }) + } + + // fetch private data for first page + useEffect(() => { + loadPrivate() + }, [tagId, viewer.id]) + + // refetch & pull to refresh + const refetch = async () => { + await refetchPublic() + loadPrivate() + } + usePullToRefresh.Register() + usePullToRefresh.Handler(refetch) + + /** + * Render + */ if (loading) { return ( From 613c15d32f4d730b18014957117cc4150bb4ad24 Mon Sep 17 00:00:00 2001 From: RoberTu Date: Tue, 21 Jul 2020 12:40:51 +0800 Subject: [PATCH 74/86] fix(types): fix types error --- .../TagDetail/Buttons/AddButton/TagArticleDialog/Content.tsx | 4 ++-- .../TagDetail/Buttons/AddButton/TagArticleDialog/index.tsx | 4 ++-- src/views/TagDetail/Buttons/AddButton/index.tsx | 4 ++-- src/views/TagDetail/Buttons/FollowButton/Follow.tsx | 4 ++-- src/views/TagDetail/Buttons/FollowButton/Unfollow.tsx | 4 ++-- src/views/TagDetail/Buttons/FollowButton/index.tsx | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/views/TagDetail/Buttons/AddButton/TagArticleDialog/Content.tsx b/src/views/TagDetail/Buttons/AddButton/TagArticleDialog/Content.tsx index 24c5f41bbd..50180280f8 100644 --- a/src/views/TagDetail/Buttons/AddButton/TagArticleDialog/Content.tsx +++ b/src/views/TagDetail/Buttons/AddButton/TagArticleDialog/Content.tsx @@ -23,7 +23,7 @@ import { parseFormSubmitErrors, translate } from '~/common/utils' import styles from './styles.css' -import { TagDetail_node_Tag } from '../../../__generated__/TagDetail' +import { TagDetailPublic_node_Tag } from '../../../__generated__/TagDetailPublic' import { AddArticlesTags } from './__generated__/AddArticlesTags' const ADD_ARTICLES_TAGS = gql` @@ -65,7 +65,7 @@ const DropdownContent = ({ interface TagArticleDialogContentProps { closeDialog: () => void forSelected?: boolean - tag: TagDetail_node_Tag + tag: TagDetailPublic_node_Tag } interface FormValues { diff --git a/src/views/TagDetail/Buttons/AddButton/TagArticleDialog/index.tsx b/src/views/TagDetail/Buttons/AddButton/TagArticleDialog/index.tsx index 362f08a5f0..278d65ec98 100644 --- a/src/views/TagDetail/Buttons/AddButton/TagArticleDialog/index.tsx +++ b/src/views/TagDetail/Buttons/AddButton/TagArticleDialog/index.tsx @@ -4,12 +4,12 @@ import { Dialog } from '~/components' import Content from './Content' -import { TagDetail_node_Tag } from '../../../__generated__/TagDetail' +import { TagDetailPublic_node_Tag } from '../../../__generated__/TagDetailPublic' interface TagArticleDialogProps { children: ({ open }: { open: () => void }) => React.ReactNode forSelected?: boolean - tag: TagDetail_node_Tag + tag: TagDetailPublic_node_Tag } const TagArticleDialog = ({ diff --git a/src/views/TagDetail/Buttons/AddButton/index.tsx b/src/views/TagDetail/Buttons/AddButton/index.tsx index 86b3978ed3..7c3e64b040 100644 --- a/src/views/TagDetail/Buttons/AddButton/index.tsx +++ b/src/views/TagDetail/Buttons/AddButton/index.tsx @@ -29,11 +29,11 @@ import { import TagArticleDialog from './TagArticleDialog' import { CreateDraft } from '~/components/GQL/mutations/__generated__/CreateDraft' -import { TagDetail_node_Tag } from '../../__generated__/TagDetail' +import { TagDetailPublic_node_Tag } from '../../__generated__/TagDetailPublic' interface DropdownActionsProps { isMaintainer: boolean - tag: TagDetail_node_Tag + tag: TagDetailPublic_node_Tag } interface DialogProps { diff --git a/src/views/TagDetail/Buttons/FollowButton/Follow.tsx b/src/views/TagDetail/Buttons/FollowButton/Follow.tsx index 3930b67e7a..ff901c1d8a 100644 --- a/src/views/TagDetail/Buttons/FollowButton/Follow.tsx +++ b/src/views/TagDetail/Buttons/FollowButton/Follow.tsx @@ -13,10 +13,10 @@ import TOGGLE_FOLLOW_TAG from '~/components/GQL/mutations/toggleFollowTag' import updateTagFollowers from '~/components/GQL/updates/tagFollowers' import { ToggleFollowTag } from '~/components/GQL/mutations/__generated__/ToggleFollowTag' -import { FollowButtonTag as FollowButtonTagType } from './__generated__/FollowButtonTag' +import { FollowButtonTagPrivate } from './__generated__/FollowButtonTagPrivate' interface FollowProps { - tag: FollowButtonTagType + tag: FollowButtonTagPrivate } const Follow = ({ tag }: FollowProps) => { diff --git a/src/views/TagDetail/Buttons/FollowButton/Unfollow.tsx b/src/views/TagDetail/Buttons/FollowButton/Unfollow.tsx index 92b8900493..4c65de9cc7 100644 --- a/src/views/TagDetail/Buttons/FollowButton/Unfollow.tsx +++ b/src/views/TagDetail/Buttons/FollowButton/Unfollow.tsx @@ -7,10 +7,10 @@ import TOGGLE_FOLLOW_TAG from '~/components/GQL/mutations/toggleFollowTag' import updateTagFollowers from '~/components/GQL/updates/tagFollowers' import { ToggleFollowTag } from '~/components/GQL/mutations/__generated__/ToggleFollowTag' -import { FollowButtonTag as FollowButtonTagType } from './__generated__/FollowButtonTag' +import { FollowButtonTagPrivate } from './__generated__/FollowButtonTagPrivate' interface UnfollowTagProps { - tag: FollowButtonTagType + tag: FollowButtonTagPrivate } const Unfollow = ({ tag }: UnfollowTagProps) => { diff --git a/src/views/TagDetail/Buttons/FollowButton/index.tsx b/src/views/TagDetail/Buttons/FollowButton/index.tsx index 5056fc381c..5e375fb6c3 100644 --- a/src/views/TagDetail/Buttons/FollowButton/index.tsx +++ b/src/views/TagDetail/Buttons/FollowButton/index.tsx @@ -3,10 +3,10 @@ import gql from 'graphql-tag' import Follow from './Follow' import Unfollow from './Unfollow' -import { FollowButtonTag as FollowButtonTagType } from './__generated__/FollowButtonTag' +import { FollowButtonTagPrivate } from './__generated__/FollowButtonTagPrivate' interface FollowButtonProps { - tag: FollowButtonTagType + tag: FollowButtonTagPrivate } const fragments = { From 249bc129b8042a3c019412d4d047015806235e93 Mon Sep 17 00:00:00 2001 From: RoberTu Date: Tue, 21 Jul 2020 18:13:50 +0800 Subject: [PATCH 75/86] fix(layout): fix lost overwrite margins --- src/common/styles/layouts/grids.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/styles/layouts/grids.css b/src/common/styles/layouts/grids.css index d9ca380fa9..d566f32d38 100644 --- a/src/common/styles/layouts/grids.css +++ b/src/common/styles/layouts/grids.css @@ -8,9 +8,9 @@ * Row */ .l-row { + lost-center: 100%; margin-right: 1rem; margin-left: 1rem; - lost-center: 100%; @media (--sm-up) { & { From 36c0ba83e1607e1b4b5317af3fff1877a76ff6a0 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 21 Jul 2020 22:13:55 +0000 Subject: [PATCH 76/86] build(deps): bump @sentry/browser from 5.19.2 to 5.20.0 Bumps [@sentry/browser](https://github.com/getsentry/sentry-javascript) from 5.19.2 to 5.20.0. - [Release notes](https://github.com/getsentry/sentry-javascript/releases) - [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-javascript/compare/5.19.2...5.20.0) Signed-off-by: dependabot-preview[bot] --- package-lock.json | 62 +++++++++++++++++++++++------------------------ package.json | 2 +- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3249113dce..1b7457ea0d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4287,59 +4287,59 @@ } }, "@sentry/browser": { - "version": "5.19.2", - "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-5.19.2.tgz", - "integrity": "sha512-o6Z532n+0N5ANDzgR9GN+Q6CU7zVlIJvBEW234rBiB+ZZj6XwTLS1dD+JexGr8lCo8PeXI2rypKcj1jUGLVW8w==", + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-5.20.0.tgz", + "integrity": "sha512-xVPL7/RuAPcemfSzXiyPHAt4M+0BfzkdTlN+PZb6frCEo4k6E0UiN6WLsGj/iwa2gXhyfTQXtbTuP+tDuNPEJw==", "requires": { - "@sentry/core": "5.19.2", - "@sentry/types": "5.19.2", - "@sentry/utils": "5.19.2", + "@sentry/core": "5.20.0", + "@sentry/types": "5.20.0", + "@sentry/utils": "5.20.0", "tslib": "^1.9.3" } }, "@sentry/core": { - "version": "5.19.2", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.19.2.tgz", - "integrity": "sha512-sfbBsVXpA0WYJUichz5IhvqKD8xJUfQvsszrTsUKa7PQAMAboOmuh6bo8KquaVQnAZyZWZU08UduvlSV3tA7tw==", - "requires": { - "@sentry/hub": "5.19.2", - "@sentry/minimal": "5.19.2", - "@sentry/types": "5.19.2", - "@sentry/utils": "5.19.2", + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.20.0.tgz", + "integrity": "sha512-fzzWKEolc0O6H/phdDenzKs7JXDSb0sooxVn0QCUkwWSzACALQh+NR/UciOXyhyuoUiqu4zthYQx02qtGqizeQ==", + "requires": { + "@sentry/hub": "5.20.0", + "@sentry/minimal": "5.20.0", + "@sentry/types": "5.20.0", + "@sentry/utils": "5.20.0", "tslib": "^1.9.3" } }, "@sentry/hub": { - "version": "5.19.2", - "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.19.2.tgz", - "integrity": "sha512-2KkEYX4q9TDCOiaVEo2kQ1W0IXyZxJxZtIjDdFQyes9T4ubYlKHAbvCjTxHSQv37lDO4t7sOIApWG9rlkHzlEA==", + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.20.0.tgz", + "integrity": "sha512-DiU8fpjAAMOgSx5tsTekMtHPCAtSNWSNS91FFkDCqPn6fYG+/aK/hB5kTlJwr+GTM1815+WWrtXP6y2ecSmZuA==", "requires": { - "@sentry/types": "5.19.2", - "@sentry/utils": "5.19.2", + "@sentry/types": "5.20.0", + "@sentry/utils": "5.20.0", "tslib": "^1.9.3" } }, "@sentry/minimal": { - "version": "5.19.2", - "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.19.2.tgz", - "integrity": "sha512-rApEOkjy+ZmkeqEItgFvUFxe5l+dht9AumuUzq74pWp+HJqxxv9IVTusKppBsE1adjtmyhwK4O3Wr8qyc75xlw==", + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.20.0.tgz", + "integrity": "sha512-oA+0g7p3bapzjgGKQIkSjcjA85VG1HPmjxBD9wpRvNjmYuVmm80Cl1H/P+xg/hupw/kNmASAX4IOd5Z9pEeboA==", "requires": { - "@sentry/hub": "5.19.2", - "@sentry/types": "5.19.2", + "@sentry/hub": "5.20.0", + "@sentry/types": "5.20.0", "tslib": "^1.9.3" } }, "@sentry/types": { - "version": "5.19.2", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.19.2.tgz", - "integrity": "sha512-O6zkW8oM1qK5Uma9+B/UMlmlm9/gkw9MooqycWuEhIaKfDBj/yVbwb/UTiJmNkGc5VJQo0v1uXUZZQt6/Xq1GA==" + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.20.0.tgz", + "integrity": "sha512-/9tiGiXBRsOKM66HeCpt0iSF0vnAIqHzXgC97icNQIstx/ZA8tcLs9540cHDeaN0cyZUyZF1o8ECqcLXGNODWQ==" }, "@sentry/utils": { - "version": "5.19.2", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.19.2.tgz", - "integrity": "sha512-gEPkC0CJwvIWqcTcPSdIzqJkJa9N5vZzUZyBvdu1oiyJu7MfazpJEvj3whfJMysSfXJQxoJ+a1IPrA73VY23VA==", + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.20.0.tgz", + "integrity": "sha512-w0AeAzWEf35h9U9QL/4lgS9MqaTPjeSmQYNU/n4ef3FKr+u8HP68Ra7NZ0adiKgi67Yxr652kWopOLPl7CxvZg==", "requires": { - "@sentry/types": "5.19.2", + "@sentry/types": "5.20.0", "tslib": "^1.9.3" } }, diff --git a/package.json b/package.json index 18d1c54a0c..93d9bed027 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "@reach/alert": "^0.10.5", "@reach/dialog": "^0.10.5", "@reach/visually-hidden": "^0.10.4", - "@sentry/browser": "^5.19.2", + "@sentry/browser": "^5.20.0", "@stripe/react-stripe-js": "^1.1.2", "@stripe/stripe-js": "^1.8.0", "@tippyjs/react": "^4.0.4", From ebe1efa33fa4b05e5364b731f72623e9d4f50e44 Mon Sep 17 00:00:00 2001 From: RoberTu Date: Wed, 22 Jul 2020 13:15:12 +0800 Subject: [PATCH 77/86] feat(public-private-query): strip off cookie for public queries --- src/common/utils/withApollo.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/common/utils/withApollo.ts b/src/common/utils/withApollo.ts index 33c6f4d696..44e42c6f05 100644 --- a/src/common/utils/withApollo.ts +++ b/src/common/utils/withApollo.ts @@ -43,7 +43,6 @@ const persistedQueryLink = createPersistedQueryLink({ const httpLink = ({ headers }: { [key: string]: any }) => createUploadLink({ uri: process.env.NEXT_PUBLIC_API_URL, - credentials: 'include', headers, fetchOptions: { agent, @@ -68,11 +67,16 @@ const errorLink = onError(({ graphQLErrors, networkError }) => { }) const authLink = setContext((operation, { headers }) => { + const operationName = operation.operationName || '' + if (process.env.NODE_ENV !== 'production') { - console.log(`\x1b[32m[GraphQL operation]\x1b[0m`, operation.operationName) + console.log(`\x1b[32m[GraphQL operation]\x1b[0m`, operationName) } + const isPublicOperation = /Public$/.test(operationName) + return { + credentials: isPublicOperation ? 'omit' : 'include', headers: { ...headers, 'x-client-name': 'web', @@ -126,9 +130,9 @@ export default withApollo(({ ctx, headers, initialState }) => { link: ApolloLink.from([ persistedQueryLink, errorLink, - authLink, sentryLink, agentHashLink, + authLink, httpLink({ headers }), ]), cache, From 19b6c25f790ba738963f955171069ff0cb975970 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 22 Jul 2020 22:07:05 +0000 Subject: [PATCH 78/86] build(deps-dev): bump @types/lodash from 4.14.157 to 4.14.158 Bumps [@types/lodash](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/lodash) from 4.14.157 to 4.14.158. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/lodash) Signed-off-by: dependabot-preview[bot] --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1b7457ea0d..d349a5fdb0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6033,9 +6033,9 @@ "dev": true }, "@types/lodash": { - "version": "4.14.157", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.157.tgz", - "integrity": "sha512-Ft5BNFmv2pHDgxV5JDsndOWTRJ+56zte0ZpYLowp03tW+K+t8u8YMOzAnpuqPgzX6WO1XpDIUm7u04M8vdDiVQ==", + "version": "4.14.158", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.158.tgz", + "integrity": "sha512-InCEXJNTv/59yO4VSfuvNrZHt7eeNtWQEgnieIA+mIC+MOWM9arOWG2eQ8Vhk6NbOre6/BidiXhkZYeDY9U35w==", "dev": true }, "@types/long": { diff --git a/package.json b/package.json index 93d9bed027..bea5d06cc0 100644 --- a/package.json +++ b/package.json @@ -100,7 +100,7 @@ "@types/helmet": "0.0.47", "@types/jest": "^26.0.5", "@types/jump.js": "^1.0.3", - "@types/lodash": "^4.14.157", + "@types/lodash": "^4.14.158", "@types/nprogress": "0.2.0", "@types/pulltorefreshjs": "^0.1.3", "@types/react": "^16.9.43", From 980174005955583f337d9c1303a19a4ff927d22e Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 22 Jul 2020 22:08:56 +0000 Subject: [PATCH 79/86] build(deps-dev): bump nightwatch from 1.3.6 to 1.3.7 Bumps [nightwatch](https://github.com/nightwatchjs/nightwatch) from 1.3.6 to 1.3.7. - [Release notes](https://github.com/nightwatchjs/nightwatch/releases) - [Commits](https://github.com/nightwatchjs/nightwatch/compare/v1.3.6...v1.3.7) Signed-off-by: dependabot-preview[bot] --- package-lock.json | 117 ++++++++++------------------------------------ package.json | 2 +- 2 files changed, 26 insertions(+), 93 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1b7457ea0d..d67ee06648 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9097,9 +9097,9 @@ } }, "cli-spinners": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.3.0.tgz", - "integrity": "sha512-Xs2Hf2nzrvJMFKimOR7YR0QwZ8fc0u98kdtwN1eNAZzNQgH3vK2pXzff6GJtKh7S5hoJ87ECiAiZFS2fb5Ii2w==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.4.0.tgz", + "integrity": "sha512-sJAofoarcm76ZGpuooaO0eDy8saEy+YoZBLjC4h8srt4jeBnkYeOgqxgsJQTpyt2LjI5PTfLJHSL+41Yu4fEJA==", "dev": true }, "cli-table3": { @@ -11560,9 +11560,9 @@ } }, "envinfo": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.5.1.tgz", - "integrity": "sha512-hQBkDf2iO4Nv0CNHpCuSBeaSrveU6nThVxFGTrq/eDlV716UQk09zChaJae4mZRsos1x4YLY2TaH3LHUae3ZmQ==", + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.7.0.tgz", + "integrity": "sha512-XX0+kACx7HcIFhar/JjsDtDIVcC8hnzQO1Asehq+abs+v9MtzpUuujFb6eBTT4lF9j2Bh6d2XFngbFRryjUAeQ==", "dev": true }, "errno": { @@ -19078,9 +19078,9 @@ "dev": true }, "nightwatch": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/nightwatch/-/nightwatch-1.3.6.tgz", - "integrity": "sha512-61kz2mw3Ng8Rrs2CDZv6aVB+bW+oNIFXL543L9kOvOqft3zVh2j08W8ww6BxGDzmWZe1HGRXNEI5U8+I4hO4KA==", + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/nightwatch/-/nightwatch-1.3.7.tgz", + "integrity": "sha512-Cy9MJsBVNs+duREiyISKpCmR20F3VKFaus1rBUpBO+fHTh2RULW41wJlTdrXYnjUThn9DeurI/rjo1lMrYI/nQ==", "dev": true, "requires": { "assertion-error": "^1.1.0", @@ -19104,87 +19104,11 @@ "strip-ansi": "^6.0.0" }, "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, "dotenv": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-7.0.0.tgz", "integrity": "sha512-M3NhsLbV1i6HuGzBUH8vXrtxOk+tWmzWKDMbAVSUp3Zsjm7ywFeuwrUXhmhQyRK1q5B5GGy7hcXPbj3bnfZg2g==", "dev": true - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true - }, - "request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "dev": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } } } }, @@ -19668,9 +19592,9 @@ } }, "ora": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/ora/-/ora-4.0.4.tgz", - "integrity": "sha512-77iGeVU1cIdRhgFzCK8aw1fbtT1B/iZAvWjS+l/o1x0RShMgxHUZaD2yDpWsNCPwXg9z1ZA78Kbdvr8kBmG/Ww==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/ora/-/ora-4.0.5.tgz", + "integrity": "sha512-jCDgm9DqvRcNIAEv2wZPrh7E5PcQiDUnbnWbAfu4NGAE2ZNqPFbDixmWldy1YG2QfLeQhuiu6/h5VRrk6cG50w==", "dev": true, "requires": { "chalk": "^3.0.0", @@ -22838,17 +22762,26 @@ } }, "request-promise": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.5.tgz", - "integrity": "sha512-ZgnepCykFdmpq86fKGwqntyTiUrHycALuGggpyCZwMvGaZWgxW6yagT0FHkgo5LzYvOaCNvxYwWYIjevSH1EDg==", + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.6.tgz", + "integrity": "sha512-HCHI3DJJUakkOr8fNoCc73E5nU5bqITjOYFMDrKHYOXWXrgD/SBaC7LjwuPymUprRyuF06UK7hd/lMHkmUXglQ==", "dev": true, "requires": { "bluebird": "^3.5.0", - "request-promise-core": "1.1.3", + "request-promise-core": "1.1.4", "stealthy-require": "^1.1.1", "tough-cookie": "^2.3.3" }, "dependencies": { + "request-promise-core": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", + "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", + "dev": true, + "requires": { + "lodash": "^4.17.19" + } + }, "tough-cookie": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", diff --git a/package.json b/package.json index 93d9bed027..135f45986d 100644 --- a/package.json +++ b/package.json @@ -132,7 +132,7 @@ "next-compose-plugins": "^2.2.0", "next-offline": "^5.0.0-beta.10", "next-optimized-images": "^2.6.1", - "nightwatch": "^1.3.6", + "nightwatch": "^1.3.7", "nightwatch-api": "^3.0.1", "postcss-calc": "^7.0.2", "postcss-color-function": "^4.1.0", From e6f5e5d19999d7c153414ece81977df17aa57cf3 Mon Sep 17 00:00:00 2001 From: RoberTu Date: Thu, 23 Jul 2020 13:25:19 +0800 Subject: [PATCH 80/86] chore(env): update `.env.dev` --- .env.dev | 6 +++--- package.json | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.env.dev b/.env.dev index 78c0e23000..280de1027a 100644 --- a/.env.dev +++ b/.env.dev @@ -8,9 +8,9 @@ NEXT_PUBLIC_SITE_DOMAIN=https://web-develop.matters.news NEXT_PUBLIC_ASSET_DOMAIN=https://assets-develop.matters.news -NEXT_PUBLIC_API_URL=https://server-stage.matters.news/graphql -NEXT_PUBLIC_WS_URL=wss://server-stage.matters.news/graphql -NEXT_PUBLIC_OAUTH_URL=https://server-stage.matters.news/oauth +NEXT_PUBLIC_API_URL=https://server-develop.matters.news/graphql +NEXT_PUBLIC_WS_URL=wss://server-develop.matters.news/graphql +NEXT_PUBLIC_OAUTH_URL=https://server-develop.matters.news/oauth NEXT_PUBLIC_SEGMENT_KEY=3gE20MjzN9qncFqlKV0pDvNO7Cp2gWU3 NEXT_PUBLIC_FB_APP_ID=823885921293850 NEXT_PUBLIC_SENTRY_DSN=https://409be482d1da4670879048d7d943c38e@sentry.matters.one/2 diff --git a/package.json b/package.json index 93d9bed027..e1c2a219b6 100644 --- a/package.json +++ b/package.json @@ -3,12 +3,12 @@ "version": "3.10.2", "description": "codebase of Matters' website", "sideEffects": false, - "author": "", + "author": "Matters ", "engines": { "node": ">=12.16", "npm": ">=6.14" }, - "license": "ISC", + "license": "Apache-2.0", "scripts": { "dev": "PORT=\"${PORT:-3000}\"; next -p $PORT", "start": "PORT=\"${PORT:-3000}\"; next start -p $PORT", From 6aceb2cec7eb8bcefd2b6fd567d81b57057b39d8 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 23 Jul 2020 22:01:57 +0000 Subject: [PATCH 81/86] build(deps-dev): bump chromedriver from 84.0.0 to 84.0.1 Bumps [chromedriver](https://github.com/giggio/node-chromedriver) from 84.0.0 to 84.0.1. - [Release notes](https://github.com/giggio/node-chromedriver/releases) - [Commits](https://github.com/giggio/node-chromedriver/commits) Signed-off-by: dependabot-preview[bot] --- package-lock.json | 35 ++++++++++++++++++++++------------- package.json | 2 +- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/package-lock.json b/package-lock.json index 779e534ecb..837c55541f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8959,37 +8959,46 @@ } }, "chromedriver": { - "version": "84.0.0", - "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-84.0.0.tgz", - "integrity": "sha512-fNX9eT1C38D1W8r5ss9ty42eDK+GIkCZVKukfeDs0XSBeKfyT0o/vbMdPr9MUkWQ+vIcFAS5hFGp9E3+xoaMeQ==", + "version": "84.0.1", + "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-84.0.1.tgz", + "integrity": "sha512-iJ6Y680yp58+KlAPS5YgYe3oePVFf8jY5k4YoczhXkT0p/mQZKfGNkGG/Xc0LjGWDQRTgZwXg66hOXoApIQecg==", "dev": true, "requires": { "@testim/chrome-version": "^1.0.7", "axios": "^0.19.2", "del": "^5.1.0", - "extract-zip": "^2.0.0", - "https-proxy-agent": "^2.2.4", + "extract-zip": "^2.0.1", + "https-proxy-agent": "^5.0.0", "mkdirp": "^1.0.4", "tcp-port-used": "^1.0.1" }, "dependencies": { + "agent-base": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.1.tgz", + "integrity": "sha512-01q25QQDwLSsyfhrKbn8yuur+JNw0H+0Y4JiGIKd3z9aYk/w/2kxD/Upc+t2ZBBSUNff50VjPsSW2YxM8QYKVg==", + "dev": true, + "requires": { + "debug": "4" + } + }, "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "dev": true, "requires": { "ms": "^2.1.1" } }, "https-proxy-agent": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", - "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", "dev": true, "requires": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" + "agent-base": "6", + "debug": "4" } }, "mkdirp": { diff --git a/package.json b/package.json index 66aa35de95..ff709a5a32 100644 --- a/package.json +++ b/package.json @@ -117,7 +117,7 @@ "babel-jest": "^26.1.0", "babel-plugin-dynamic-import-node": "^2.3.3", "babel-polyfill": "^6.26.0", - "chromedriver": "^84.0.0", + "chromedriver": "^84.0.1", "cucumber": "^6.0.5", "cucumber-pretty": "^6.0.0", "cz-conventional-changelog": "^3.2.0", From efa3d7b5aa76a0c579616934589aa6cecb575297 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 23 Jul 2020 22:04:41 +0000 Subject: [PATCH 82/86] build(deps): bump firebase from 7.16.1 to 7.17.0 Bumps [firebase](https://github.com/firebase/firebase-js-sdk) from 7.16.1 to 7.17.0. - [Release notes](https://github.com/firebase/firebase-js-sdk/releases) - [Commits](https://github.com/firebase/firebase-js-sdk/compare/firebase@7.16.1...firebase@7.17.0) Signed-off-by: dependabot-preview[bot] --- package-lock.json | 28 ++++++++++++++-------------- package.json | 2 +- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/package-lock.json b/package-lock.json index 779e534ecb..53c3176da7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1749,9 +1749,9 @@ } }, "@firebase/analytics": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.3.9.tgz", - "integrity": "sha512-l4dNskm8uQ+UqO6Lw+fuyO1enZBXUV6xNMxeVABEnVrp3wOP90KKb/ZwYgleAxF1It52lorcTtkA1YFpv3iEIQ==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.4.0.tgz", + "integrity": "sha512-8DC2OBXGYxeeRxCh6eFnrrswNcKm2WsD8EeqGcl0F1P7J0bJ4Q+WpP3DvxofQZ/PtVHdAhzmfmt9r6Xa9mHnrQ==", "requires": { "@firebase/analytics-types": "0.3.1", "@firebase/component": "0.1.16", @@ -1970,9 +1970,9 @@ "integrity": "sha512-G96qnF3RYGbZsTRut7NBX0sxyczxt1uyCgXQuH/eAfUCngxjEGcZQnBdy6mvSdqdJh5mC31rWPO4v9/s7HwtzA==" }, "@firebase/storage": { - "version": "0.3.39", - "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.3.39.tgz", - "integrity": "sha512-uTE8kROU/NMas+0i2oK0U9LuAlDzt+Cis0ErmYPlbCvmFqpFdyu3TtlO5MYNoxGLaBjEyxb18NJZai9lNMXFlQ==", + "version": "0.3.40", + "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.3.40.tgz", + "integrity": "sha512-xTUvSSXh8tNSlch4V+kNbw736H0z/lbW3rHlx1kZVnT8V5M4bXE+TEcG4WpqvcWH3p+N6N1bUorkDbOFgBrztw==", "requires": { "@firebase/component": "0.1.16", "@firebase/storage-types": "0.3.13", @@ -1999,9 +1999,9 @@ "integrity": "sha512-XcdMT5PSZHiuf7LJIhzKIe+RyYa25S3LHRRvLnZc6iFjwXkrSDJ8J/HWO6VT8d2ZTbawp3VcLEjRF/VN8glCrA==" }, "@grpc/grpc-js": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.1.2.tgz", - "integrity": "sha512-k2u86Bkm/3xrjUaSWeIyzXScBt/cC8uE7BznR0cpueQi11R33W6qfJdMrkrsmSHirp5likR55JSXUrcWG6ybHA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.1.3.tgz", + "integrity": "sha512-HtOsk2YUofBcm1GkPqGzb6pwHhv+74eC2CUO229USIDKRtg30ycbZmqC+HdNtY3nHqoc9IgcRlntFgopyQoYCA==", "requires": { "semver": "^6.2.0" } @@ -12511,11 +12511,11 @@ "integrity": "sha512-H1k/ESTD2rJ3liupyqWBPjZC+LKfCGixQzz/NDN4dkgbmG1bVFyMOh7luKSkVDoyfhgvRm62pviNMPI+eJTZcQ==" }, "firebase": { - "version": "7.16.1", - "resolved": "https://registry.npmjs.org/firebase/-/firebase-7.16.1.tgz", - "integrity": "sha512-mcvFh617lWPYnx6SmwgtwmliY8P3XBi8pm0LDY4a8WPD049goCMgmIEpKkX4R3gZ2noz2rVrxSUfodENPpttLg==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/firebase/-/firebase-7.17.0.tgz", + "integrity": "sha512-+y7c1pCj8xp98CIDhVjg0rKhGtsFskGB8hyhjsyp549Upwa0cropdK5emCFTmMIbvDjZmP8rTuuDXPBeREAaCg==", "requires": { - "@firebase/analytics": "0.3.9", + "@firebase/analytics": "0.4.0", "@firebase/app": "0.6.8", "@firebase/app-types": "0.6.1", "@firebase/auth": "0.14.9", @@ -12527,7 +12527,7 @@ "@firebase/performance": "0.3.9", "@firebase/polyfill": "0.3.36", "@firebase/remote-config": "0.1.25", - "@firebase/storage": "0.3.39", + "@firebase/storage": "0.3.40", "@firebase/util": "0.2.50" } }, diff --git a/package.json b/package.json index 66aa35de95..121806556e 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "date-fns": "^2.15.0", "express": "^4.17.1", "fingerprintjs2": "^2.1.0", - "firebase": "^7.16.1", + "firebase": "^7.17.0", "formik": "^2.1.5", "graphql": "^14.7.0", "graphql-tag": "^2.10.4", From b046a35e8b3e7012e8d62485eb49d796e0efda8d Mon Sep 17 00:00:00 2001 From: RoberTu Date: Fri, 24 Jul 2020 15:12:38 +0800 Subject: [PATCH 83/86] fix(appreciation): fix appreciation button has a wrong state --- src/components/Protected/index.tsx | 3 ++- .../ArticleDetail/AppreciationButton/index.tsx | 5 +++-- src/views/ArticleDetail/index.tsx | 6 +++--- src/views/OAuth/Authorize/index.tsx | 14 ++++++-------- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/components/Protected/index.tsx b/src/components/Protected/index.tsx index 85e4d308b6..82395f9051 100644 --- a/src/components/Protected/index.tsx +++ b/src/components/Protected/index.tsx @@ -13,7 +13,8 @@ export const Protected: React.FC = ({ children }) => { } }, [viewer.privateFetched]) - if (viewer.isAuthed) { + + if (viewer.isAuthed && viewer.privateFetched) { return <>{children} } diff --git a/src/views/ArticleDetail/AppreciationButton/index.tsx b/src/views/ArticleDetail/AppreciationButton/index.tsx index ba4daf5488..1fcb3d15ad 100644 --- a/src/views/ArticleDetail/AppreciationButton/index.tsx +++ b/src/views/ArticleDetail/AppreciationButton/index.tsx @@ -75,11 +75,12 @@ const AppreciationButton = ({ article }: AppreciationButtonProps) => { // bundle appreciations const [amount, setAmount] = useState(0) const [sendAppreciation] = useMutation(APPRECIATE_ARTICLE) + const hasAppreciate = article.hasAppreciate const limit = article.appreciateLimit const left = (article.appreciateLeft || 0) - amount const total = article.appreciationsReceivedTotal + amount - const appreciatedCount = limit - left + const appreciatedCount = hasAppreciate || amount ? limit - left : 0 const [debouncedSendAppreciation] = useDebouncedCallback(async () => { try { await sendAppreciation({ @@ -130,7 +131,7 @@ const AppreciationButton = ({ article }: AppreciationButtonProps) => { /** * Appreciate Button */ - if (canAppreciate) { + if (canAppreciate || !hasAppreciate) { return ( { // fetch private data const loadPrivate = () => { - if (!viewer.id || !article) { + if (!viewer.id || !article || !article?.mediaHash) { return } @@ -108,7 +108,7 @@ const ArticleDetail = () => { query: ARTICLE_DETAIL_PRIVATE, fetchPolicy: 'network-only', variables: { - mediaHash, + mediaHash: article?.mediaHash, includeContent: article.state !== 'active' && isAuthor, }, }) @@ -116,7 +116,7 @@ const ArticleDetail = () => { useEffect(() => { loadPrivate() - }, [mediaHash, !!article, viewer.id]) + }, [article?.mediaHash, viewer.id]) // translation const [translate, setTranslate] = useState(false) diff --git a/src/views/OAuth/Authorize/index.tsx b/src/views/OAuth/Authorize/index.tsx index 6765861f4e..b4506b894b 100644 --- a/src/views/OAuth/Authorize/index.tsx +++ b/src/views/OAuth/Authorize/index.tsx @@ -136,14 +136,12 @@ const OAuthAuthorize = () => { - {/* FIXME: only render at CSR to get correct `appendTarget` */} - {process.browser && ( - - - - - - )} + + + + + +

From 534fcf12175f1d38dc531b06b533aadd2b9096e7 Mon Sep 17 00:00:00 2001 From: RoberTu Date: Fri, 24 Jul 2020 16:13:13 +0800 Subject: [PATCH 84/86] feat(text): remove countdown text for draft publishing --- .../Forms/PaymentForm/Checkout/CardSection.tsx | 7 ++++++- .../DraftDetail/PublishState/PendingState.tsx | 17 ++++------------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/components/Forms/PaymentForm/Checkout/CardSection.tsx b/src/components/Forms/PaymentForm/Checkout/CardSection.tsx index b69877047d..7f3ed238f7 100644 --- a/src/components/Forms/PaymentForm/Checkout/CardSection.tsx +++ b/src/components/Forms/PaymentForm/Checkout/CardSection.tsx @@ -47,7 +47,12 @@ const CardSection: React.FC = ({ error, onChange }) => { } + label={ + + } /> diff --git a/src/views/Me/DraftDetail/PublishState/PendingState.tsx b/src/views/Me/DraftDetail/PublishState/PendingState.tsx index 87c60974fa..f76257245d 100644 --- a/src/views/Me/DraftDetail/PublishState/PendingState.tsx +++ b/src/views/Me/DraftDetail/PublishState/PendingState.tsx @@ -1,27 +1,21 @@ import { useQuery } from '@apollo/react-hooks' -import { Toast, Translate, useCountdown } from '~/components' +import { Toast, Translate } from '~/components' import DRAFT_PUBLISH_STATE from '~/components/GQL/queries/draftPublishState' -import { TEXT } from '~/common/enums' - import { PublishStateDraft } from '~/components/GQL/fragments/__generated__/PublishStateDraft' import { DraftPublishState } from '~/components/GQL/queries/__generated__/DraftPublishState' const PendingState = ({ draft }: { draft: PublishStateDraft }) => { const scheduledAt = draft.scheduledAt - const { - countdown: { timeLeft }, - formattedTimeLeft, - } = useCountdown({ timeLeft: Date.parse(scheduledAt) - Date.now() }) - const isPublishing = !scheduledAt || !timeLeft || timeLeft <= 0 + const isPublishing = !scheduledAt || Date.parse(scheduledAt) <= Date.now() useQuery(DRAFT_PUBLISH_STATE, { variables: { id: draft.id }, pollInterval: 1000 * 2, errorPolicy: 'none', fetchPolicy: 'network-only', - skip: !process.browser || !isPublishing, + skip: !process.browser, }) return ( @@ -31,10 +25,7 @@ const PendingState = ({ draft }: { draft: PublishStateDraft }) => { isPublishing ? ( ) : ( - + ) } subDescription={ From fb9826d11bd4cfccea346cc86e008c3e90b051d9 Mon Sep 17 00:00:00 2001 From: RoberTu Date: Fri, 24 Jul 2020 19:21:14 +0800 Subject: [PATCH 85/86] fix(appreciation): fix incorrect rendering of appreciation button --- src/components/Protected/index.tsx | 1 - src/views/ArticleDetail/AppreciationButton/index.tsx | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/Protected/index.tsx b/src/components/Protected/index.tsx index 82395f9051..ba6fc2c755 100644 --- a/src/components/Protected/index.tsx +++ b/src/components/Protected/index.tsx @@ -13,7 +13,6 @@ export const Protected: React.FC = ({ children }) => { } }, [viewer.privateFetched]) - if (viewer.isAuthed && viewer.privateFetched) { return <>{children} } diff --git a/src/views/ArticleDetail/AppreciationButton/index.tsx b/src/views/ArticleDetail/AppreciationButton/index.tsx index 1fcb3d15ad..b2739fd5f9 100644 --- a/src/views/ArticleDetail/AppreciationButton/index.tsx +++ b/src/views/ArticleDetail/AppreciationButton/index.tsx @@ -131,7 +131,7 @@ const AppreciationButton = ({ article }: AppreciationButtonProps) => { /** * Appreciate Button */ - if (canAppreciate || !hasAppreciate) { + if (canAppreciate || (!hasAppreciate && amount <= 0)) { return ( Date: Fri, 24 Jul 2020 19:36:04 +0800 Subject: [PATCH 86/86] chore(release): v3.11.0 --- package.json | 2 +- src/components/Protected/index.tsx | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index 44531eaf44..5bfbd559f5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matters-web", - "version": "3.10.2", + "version": "3.11.0", "description": "codebase of Matters' website", "sideEffects": false, "author": "Matters ", diff --git a/src/components/Protected/index.tsx b/src/components/Protected/index.tsx index 82395f9051..ba6fc2c755 100644 --- a/src/components/Protected/index.tsx +++ b/src/components/Protected/index.tsx @@ -13,7 +13,6 @@ export const Protected: React.FC = ({ children }) => { } }, [viewer.privateFetched]) - if (viewer.isAuthed && viewer.privateFetched) { return <>{children} }