From 730be01e74f8239e524d393543845021ca3a86ec Mon Sep 17 00:00:00 2001 From: RoberTu Date: Thu, 24 Oct 2019 15:59:15 +0800 Subject: [PATCH 1/7] Add block user dropdown button --- public/static/icons/block.svg | 3 + public/static/icons/unblock.svg | 3 + src/common/enums/text.ts | 4 + .../Button/BlockUser/Dropdown/index.tsx | 107 ++++++++++++++++++ .../Button/BlockUser/Dropdown/styles.css | 3 + .../{ShareButton => Button/Share}/Douban.tsx | 0 .../{ShareButton => Button/Share}/Email.tsx | 0 .../Share}/Facebook.tsx | 0 .../{ShareButton => Button/Share}/LINE.tsx | 0 .../Share}/ShareModal.tsx | 0 .../Share}/Telegram.tsx | 0 .../{ShareButton => Button/Share}/Twitter.tsx | 0 .../{ShareButton => Button/Share}/WeChat.tsx | 0 .../{ShareButton => Button/Share}/Weibo.tsx | 0 .../Share}/WhatsApp.tsx | 0 .../{ShareButton => Button/Share}/index.tsx | 0 .../{ShareButton => Button/Share}/styles.css | 0 .../CommentDigest/DropdownActions/index.tsx | 20 +++- src/components/GQL/mutations/blockUser.ts | 10 ++ src/components/GQL/mutations/unblockUser.ts | 10 ++ .../UserProfile/DropdownActions/index.tsx | 64 +++++++++++ src/components/UserProfile/index.tsx | 10 +- src/views/ArticleDetail/Toolbar/index.tsx | 2 +- src/views/ArticleDetail/index.tsx | 2 +- 24 files changed, 231 insertions(+), 7 deletions(-) create mode 100644 public/static/icons/block.svg create mode 100644 public/static/icons/unblock.svg create mode 100644 src/components/Button/BlockUser/Dropdown/index.tsx create mode 100644 src/components/Button/BlockUser/Dropdown/styles.css rename src/components/{ShareButton => Button/Share}/Douban.tsx (100%) rename src/components/{ShareButton => Button/Share}/Email.tsx (100%) rename src/components/{ShareButton => Button/Share}/Facebook.tsx (100%) rename src/components/{ShareButton => Button/Share}/LINE.tsx (100%) rename src/components/{ShareButton => Button/Share}/ShareModal.tsx (100%) rename src/components/{ShareButton => Button/Share}/Telegram.tsx (100%) rename src/components/{ShareButton => Button/Share}/Twitter.tsx (100%) rename src/components/{ShareButton => Button/Share}/WeChat.tsx (100%) rename src/components/{ShareButton => Button/Share}/Weibo.tsx (100%) rename src/components/{ShareButton => Button/Share}/WhatsApp.tsx (100%) rename src/components/{ShareButton => Button/Share}/index.tsx (100%) rename src/components/{ShareButton => Button/Share}/styles.css (100%) create mode 100644 src/components/GQL/mutations/blockUser.ts create mode 100644 src/components/GQL/mutations/unblockUser.ts create mode 100644 src/components/UserProfile/DropdownActions/index.tsx diff --git a/public/static/icons/block.svg b/public/static/icons/block.svg new file mode 100644 index 0000000000..e3d5551948 --- /dev/null +++ b/public/static/icons/block.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/static/icons/unblock.svg b/public/static/icons/unblock.svg new file mode 100644 index 0000000000..c0891ee4fb --- /dev/null +++ b/public/static/icons/unblock.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/common/enums/text.ts b/src/common/enums/text.ts index a18bf28ad9..a1a4d66769 100644 --- a/src/common/enums/text.ts +++ b/src/common/enums/text.ts @@ -130,6 +130,8 @@ export const TEXT = { articleFingerprint: '作品指紋', copySuccess: '複製成功', copy: '複製', + block: '屏蔽用戶', + unblock: '取消屏蔽', pin: '喜歡回應', unpin: '取消精選', emptySearchResults: '沒有找到你搜索的內容', @@ -298,6 +300,8 @@ export const TEXT = { articleFingerprint: '作品指纹', copySuccess: '复制成功', copy: '复制', + block: '屏蔽用户', + unblock: '取消屏蔽', pin: '喜欢回应', unpin: '取消精选', emptySearchResults: '没有找到你搜寻的内容', diff --git a/src/components/Button/BlockUser/Dropdown/index.tsx b/src/components/Button/BlockUser/Dropdown/index.tsx new file mode 100644 index 0000000000..06032315d0 --- /dev/null +++ b/src/components/Button/BlockUser/Dropdown/index.tsx @@ -0,0 +1,107 @@ +import gql from 'graphql-tag' + +import { Icon, TextIcon, Translate } from '~/components' +import { useMutation } from '~/components/GQL' +import { BlockUser } from '~/components/GQL/mutations/__generated__/BlockUser' +import { UnblockUser } from '~/components/GQL/mutations/__generated__/UnblockUser' +import BLOCK_USER from '~/components/GQL/mutations/blockUser' +import UNBLOCK_USER from '~/components/GQL/mutations/unblockUser' + +import { TEXT } from '~/common/enums' +import ICON_BLOCK from '~/static/icons/block.svg?sprite' +import ICON_UNBLOCK from '~/static/icons/unblock.svg?sprite' + +import { BlockButtonUser } from './__generated__/BlockButtonUser' +import styles from './styles.css' + +const fragments = { + user: gql` + fragment BlockButtonUser on User { + id + isBlocked + } + ` +} + +const TextIconBlock = () => ( + } + spacing="tight" + > + + +) + +const TextIconUnblock = () => ( + + } + spacing="tight" + > + + +) + +const BlockUserButton = ({ + user, + hideDropdown +}: { + user: BlockButtonUser + hideDropdown: () => void +}) => { + const [blockUser] = useMutation(BLOCK_USER, { + variables: { id: user.id }, + optimisticResponse: { + blockUser: { + id: user.id, + isBlocked: true, + __typename: 'User' + } + } + }) + const [unblockUser] = useMutation(UNBLOCK_USER, { + variables: { id: user.id }, + optimisticResponse: { + unblockUser: { + id: user.id, + isBlocked: false, + __typename: 'User' + } + } + }) + + if (user.isBlocked) { + return ( + + ) + } + + return ( + + ) +} + +BlockUserButton.fragments = fragments + +export default BlockUserButton diff --git a/src/components/Button/BlockUser/Dropdown/styles.css b/src/components/Button/BlockUser/Dropdown/styles.css new file mode 100644 index 0000000000..5dbf2fe43e --- /dev/null +++ b/src/components/Button/BlockUser/Dropdown/styles.css @@ -0,0 +1,3 @@ +button { + font-size: var(--font-size-sm); +} diff --git a/src/components/ShareButton/Douban.tsx b/src/components/Button/Share/Douban.tsx similarity index 100% rename from src/components/ShareButton/Douban.tsx rename to src/components/Button/Share/Douban.tsx diff --git a/src/components/ShareButton/Email.tsx b/src/components/Button/Share/Email.tsx similarity index 100% rename from src/components/ShareButton/Email.tsx rename to src/components/Button/Share/Email.tsx diff --git a/src/components/ShareButton/Facebook.tsx b/src/components/Button/Share/Facebook.tsx similarity index 100% rename from src/components/ShareButton/Facebook.tsx rename to src/components/Button/Share/Facebook.tsx diff --git a/src/components/ShareButton/LINE.tsx b/src/components/Button/Share/LINE.tsx similarity index 100% rename from src/components/ShareButton/LINE.tsx rename to src/components/Button/Share/LINE.tsx diff --git a/src/components/ShareButton/ShareModal.tsx b/src/components/Button/Share/ShareModal.tsx similarity index 100% rename from src/components/ShareButton/ShareModal.tsx rename to src/components/Button/Share/ShareModal.tsx diff --git a/src/components/ShareButton/Telegram.tsx b/src/components/Button/Share/Telegram.tsx similarity index 100% rename from src/components/ShareButton/Telegram.tsx rename to src/components/Button/Share/Telegram.tsx diff --git a/src/components/ShareButton/Twitter.tsx b/src/components/Button/Share/Twitter.tsx similarity index 100% rename from src/components/ShareButton/Twitter.tsx rename to src/components/Button/Share/Twitter.tsx diff --git a/src/components/ShareButton/WeChat.tsx b/src/components/Button/Share/WeChat.tsx similarity index 100% rename from src/components/ShareButton/WeChat.tsx rename to src/components/Button/Share/WeChat.tsx diff --git a/src/components/ShareButton/Weibo.tsx b/src/components/Button/Share/Weibo.tsx similarity index 100% rename from src/components/ShareButton/Weibo.tsx rename to src/components/Button/Share/Weibo.tsx diff --git a/src/components/ShareButton/WhatsApp.tsx b/src/components/Button/Share/WhatsApp.tsx similarity index 100% rename from src/components/ShareButton/WhatsApp.tsx rename to src/components/Button/Share/WhatsApp.tsx diff --git a/src/components/ShareButton/index.tsx b/src/components/Button/Share/index.tsx similarity index 100% rename from src/components/ShareButton/index.tsx rename to src/components/Button/Share/index.tsx diff --git a/src/components/ShareButton/styles.css b/src/components/Button/Share/styles.css similarity index 100% rename from src/components/ShareButton/styles.css rename to src/components/Button/Share/styles.css diff --git a/src/components/CommentDigest/DropdownActions/index.tsx b/src/components/CommentDigest/DropdownActions/index.tsx index de0be77ce2..9b45f3fc20 100644 --- a/src/components/CommentDigest/DropdownActions/index.tsx +++ b/src/components/CommentDigest/DropdownActions/index.tsx @@ -2,6 +2,7 @@ import gql from 'graphql-tag' import { useContext, useState } from 'react' import { Dropdown, Icon, Menu, PopperInstance } from '~/components' +import BlockUserButton from '~/components/Button/BlockUser/Dropdown' import { ViewerContext } from '~/components/Viewer' import ICON_MORE_SMALL from '~/static/icons/more-small.svg?sprite' @@ -19,6 +20,7 @@ const fragments = { state author { id + ...BlockButtonUser } parentComment { id @@ -33,6 +35,7 @@ const fragments = { ...PinButtonComment } ${PinButton.fragments.comment} + ${BlockUserButton.fragments.user} ` } @@ -43,13 +46,15 @@ const DropdownContent: React.FC<{ isShowPinButton: boolean isShowEditButton: boolean isShowDeleteButton: boolean + isShowBlockUserButton: boolean }> = ({ comment, editComment, hideDropdown, isShowPinButton, isShowEditButton, - isShowDeleteButton + isShowDeleteButton, + isShowBlockUserButton }) => { return ( @@ -73,6 +78,11 @@ const DropdownContent: React.FC<{ )} + {isShowBlockUserButton && ( + + + + )} ) } @@ -100,12 +110,17 @@ const DropdownActions = ({ const isCommentAuthor = viewer.id === comment.author.id const isActive = comment.state === 'active' const isDescendantComment = comment.parentComment + const isShowPinButton = isArticleAuthor && isActive && !isDescendantComment const isShowEditButton = isCommentAuthor && !!editComment && isActive const isShowDeleteButton = isCommentAuthor && isActive + const isShowBlockUserButton = !isCommentAuthor if ( - (!isShowPinButton && !isShowEditButton && !isShowDeleteButton) || + (!isShowPinButton && + !isShowEditButton && + !isShowDeleteButton && + !isShowBlockUserButton) || viewer.isInactive ) { return null @@ -121,6 +136,7 @@ const DropdownActions = ({ isShowPinButton={isShowPinButton} isShowEditButton={isShowEditButton} isShowDeleteButton={isShowDeleteButton} + isShowBlockUserButton={isShowBlockUserButton} /> } trigger="click" diff --git a/src/components/GQL/mutations/blockUser.ts b/src/components/GQL/mutations/blockUser.ts new file mode 100644 index 0000000000..ce44ca61b2 --- /dev/null +++ b/src/components/GQL/mutations/blockUser.ts @@ -0,0 +1,10 @@ +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/unblockUser.ts b/src/components/GQL/mutations/unblockUser.ts new file mode 100644 index 0000000000..f753ecb950 --- /dev/null +++ b/src/components/GQL/mutations/unblockUser.ts @@ -0,0 +1,10 @@ +import gql from 'graphql-tag' + +export default gql` + mutation UnblockUser($id: ID!) { + unblockUser(input: { id: $id }) { + id + isBlocked + } + } +` diff --git a/src/components/UserProfile/DropdownActions/index.tsx b/src/components/UserProfile/DropdownActions/index.tsx new file mode 100644 index 0000000000..de85be1dbb --- /dev/null +++ b/src/components/UserProfile/DropdownActions/index.tsx @@ -0,0 +1,64 @@ +import gql from 'graphql-tag' +import { useState } from 'react' + +import { Dropdown, Icon, Menu, PopperInstance } from '~/components' +import BlockUserButton from '~/components/Button/BlockUser/Dropdown' + +import ICON_MORE_SMALL from '~/static/icons/more-small.svg?sprite' + +import { DropdownActionsUser } from './__generated__/DropdownActionsUser' + +const fragments = { + user: gql` + fragment DropdownActionsUser on User { + id + ...BlockButtonUser + } + ${BlockUserButton.fragments.user} + ` +} + +const DropdownContent: React.FC<{ + user: DropdownActionsUser + hideDropdown: () => void +}> = ({ user, hideDropdown }) => { + return ( + + + + + + ) +} + +const DropdownActions = ({ user }: { user: DropdownActionsUser }) => { + const [instance, setInstance] = useState(null) + const hideDropdown = () => { + if (!instance) { + return + } + instance.hide() + } + + return ( + } + trigger="click" + onCreate={setInstance} + placement="bottom-end" + zIndex={301} + > + + + ) +} + +DropdownActions.fragments = fragments + +export default DropdownActions diff --git a/src/components/UserProfile/index.tsx b/src/components/UserProfile/index.tsx index 8f4590128a..01a797159d 100644 --- a/src/components/UserProfile/index.tsx +++ b/src/components/UserProfile/index.tsx @@ -9,9 +9,10 @@ import { useQuery } from 'react-apollo' import { Avatar, Placeholder, Tooltip, Translate } from '~/components' import { FollowButton } from '~/components/Button/Follow' +import ShareButton from '~/components/Button/Share' +import ShareModal from '~/components/Button/Share/ShareModal' import { Icon } from '~/components/Icon' -import ShareButton from '~/components/ShareButton' -import ShareModal from '~/components/ShareButton/ShareModal' +import Throw404 from '~/components/Throw404' import { UserProfileEditor } from '~/components/UserProfileEditor' import { ViewerContext } from '~/components/Viewer' @@ -19,11 +20,11 @@ import { TEXT } from '~/common/enums' import { getQuery, numAbbr, toPath } from '~/common/utils' import ICON_SEED_BADGE from '~/static/icons/early-user-badge.svg?sprite' -import Throw404 from '../Throw404' import { MeProfileUser } from './__generated__/MeProfileUser' import { UserProfileUser } from './__generated__/UserProfileUser' import Cover from './Cover' import Description from './Description' +import DropdownActions from './DropdownActions' import EditProfileButton from './EditProfileButton' import styles from './styles.css' @@ -51,9 +52,11 @@ const fragments = { } ...AvatarUser ...FollowButtonUser @skip(if: $isMe) + ...DropdownActionsUser } ${Avatar.fragments.user} ${FollowButton.fragments.user} + ${DropdownActions.fragments.user} ` } @@ -220,6 +223,7 @@ const BaseUserProfile = () => { {isMe && !viewer.isInactive && ( )} + {!isMe && } diff --git a/src/views/ArticleDetail/Toolbar/index.tsx b/src/views/ArticleDetail/Toolbar/index.tsx index eb2ea1e1e6..577fd7d421 100644 --- a/src/views/ArticleDetail/Toolbar/index.tsx +++ b/src/views/ArticleDetail/Toolbar/index.tsx @@ -2,7 +2,7 @@ import classNames from 'classnames' import gql from 'graphql-tag' import { BookmarkButton } from '~/components/Button/Bookmark' -import ShareButton from '~/components/ShareButton' +import ShareButton from '~/components/Button/Share' import { ToolbarArticle } from './__generated__/ToolbarArticle' import AppreciationButton from './AppreciationButton' diff --git a/src/views/ArticleDetail/index.tsx b/src/views/ArticleDetail/index.tsx index 2c0d1afe86..8ad2a65c24 100644 --- a/src/views/ArticleDetail/index.tsx +++ b/src/views/ArticleDetail/index.tsx @@ -8,13 +8,13 @@ import { Waypoint } from 'react-waypoint' import { DateTime, Footer, Head, Placeholder, Title } from '~/components' import { BookmarkButton } from '~/components/Button/Bookmark' +import ShareModal from '~/components/Button/Share/ShareModal' import { Fingerprint } from '~/components/Fingerprint' import { QueryError } from '~/components/GQL' import { ClientPreference } from '~/components/GQL/queries/__generated__/ClientPreference' import CLIENT_PREFERENCE from '~/components/GQL/queries/clientPreference' import { useImmersiveMode, useResponsive } from '~/components/Hook' import IconLive from '~/components/Icon/Live' -import ShareModal from '~/components/ShareButton/ShareModal' import Throw404 from '~/components/Throw404' import { UserDigest } from '~/components/UserDigest' import { ViewerContext } from '~/components/Viewer' From f1064cb014627fdd29fd1389b456526137e063e3 Mon Sep 17 00:00:00 2001 From: RoberTu Date: Thu, 24 Oct 2019 17:01:27 +0800 Subject: [PATCH 2/7] Add blocked page --- src/common/enums/analytics.ts | 1 + src/common/enums/route.ts | 6 + src/common/enums/text.ts | 2 + .../Button/BlockUser/Unblock/index.tsx | 62 +++++++++++ src/components/Empty/EmptyWarning.tsx | 4 +- src/components/UserDigest/FullDesc/index.tsx | 12 +- src/components/UserProfile/index.tsx | 9 ++ src/pages/MeSettingsBlocked.tsx | 9 ++ .../Me/Settings/Blocked/SettingsBlocked.tsx | 104 ++++++++++++++++++ src/views/Me/Settings/Blocked/index.tsx | 15 +++ src/views/Me/Settings/SettingsTab/index.tsx | 12 ++ 11 files changed, 231 insertions(+), 5 deletions(-) create mode 100644 src/components/Button/BlockUser/Unblock/index.tsx create mode 100644 src/pages/MeSettingsBlocked.tsx create mode 100644 src/views/Me/Settings/Blocked/SettingsBlocked.tsx create mode 100644 src/views/Me/Settings/Blocked/index.tsx diff --git a/src/common/enums/analytics.ts b/src/common/enums/analytics.ts index bdc6c5a3e7..f1b7b9a4c7 100644 --- a/src/common/enums/analytics.ts +++ b/src/common/enums/analytics.ts @@ -63,6 +63,7 @@ export const FEED_TYPE = { FOLLOWER: 'follower', APPRECIATOR: 'appreciator', SEARCH_USER: 'search-user', + BLOCK_LIST: 'block-list', // tags TAGS: 'tags', ALL_TAGS: 'all-tags', diff --git a/src/common/enums/route.ts b/src/common/enums/route.ts index eddb47f703..a5bd70a02a 100644 --- a/src/common/enums/route.ts +++ b/src/common/enums/route.ts @@ -32,6 +32,7 @@ type ROUTE_KEY = | 'ME_NOTIFICATIONS' | 'ME_SETTINGS_ACCOUNT' | 'ME_SETTINGS_NOTIFICATION' + | 'ME_SETTINGS_BLOCKED' | 'ME_DRAFT_DETAIL' // | 'EDITOR' | 'AUTH_LOGIN' @@ -210,6 +211,11 @@ export const ROUTES: Array<{ href: '/MeSettingsNotification', as: '/me/settings/notification' }, + { + key: 'ME_SETTINGS_BLOCKED', + href: '/MeSettingsBlocked', + as: '/me/settings/blocked' + }, // Draft { diff --git a/src/common/enums/text.ts b/src/common/enums/text.ts index a1a4d66769..ac7fa33856 100644 --- a/src/common/enums/text.ts +++ b/src/common/enums/text.ts @@ -121,6 +121,7 @@ export const TEXT = { setting: '設定', accountSetting: '帳戶設定', notificationSetting: '通知設定', + blockedSetting: '屏蔽用戶', walletSetting: '錢包設定', uiSetting: '介面設定', userProfile: '個人簡介', @@ -291,6 +292,7 @@ export const TEXT = { setting: '设定', accountSetting: '账户设定', notificationSetting: '通知设定', + blockedSetting: '屏蔽用户', walletSetting: '钱包设定', uiSetting: '界面设定', userProfile: '个人简介', diff --git a/src/components/Button/BlockUser/Unblock/index.tsx b/src/components/Button/BlockUser/Unblock/index.tsx new file mode 100644 index 0000000000..e258f16620 --- /dev/null +++ b/src/components/Button/BlockUser/Unblock/index.tsx @@ -0,0 +1,62 @@ +import gql from 'graphql-tag' + +import { Button, Translate } from '~/components' +import { useMutation } from '~/components/GQL' +import { UnblockUser } from '~/components/GQL/mutations/__generated__/UnblockUser' +import UNBLOCK_USER from '~/components/GQL/mutations/unblockUser' + +import { ANALYTICS_EVENTS, TEXT } from '~/common/enums' +import { analytics } from '~/common/utils' + +import { UnblockButtonUser } from './__generated__/UnblockButtonUser' + +const fragments = { + user: gql` + fragment UnblockButtonUser on User { + id + isBlocked + } + ` +} + +const Unblock = ({ + user, + size = 'small' +}: { + user: UnblockButtonUser + size?: 'small' | 'default' +}) => { + const [unblockUser] = useMutation(UNBLOCK_USER, { + variables: { id: user.id }, + optimisticResponse: { + unblockUser: { + id: user.id, + isBlocked: false, + __typename: 'User' + } + } + }) + + return ( + + ) +} + +Unblock.fragments = fragments + +export default Unblock diff --git a/src/components/Empty/EmptyWarning.tsx b/src/components/Empty/EmptyWarning.tsx index 925f3f9162..bddfcdbb6a 100644 --- a/src/components/Empty/EmptyWarning.tsx +++ b/src/components/Empty/EmptyWarning.tsx @@ -2,7 +2,7 @@ import { Empty, Icon } from '~/components' import ICON_EMPTY_WARNING from '~/static/icons/empty-warning.svg?sprite' -const EmptyFollowee = ({ description }: { description: React.ReactNode }) => ( +const EmptyWarning = ({ description }: { description: React.ReactNode }) => ( ( /> ) -export default EmptyFollowee +export default EmptyWarning diff --git a/src/components/UserDigest/FullDesc/index.tsx b/src/components/UserDigest/FullDesc/index.tsx index 8b27cbbc32..d6fa8d9802 100644 --- a/src/components/UserDigest/FullDesc/index.tsx +++ b/src/components/UserDigest/FullDesc/index.tsx @@ -4,6 +4,7 @@ import Link from 'next/link' import { Icon, TextIcon } from '~/components' import { Avatar } from '~/components/Avatar' +import UnblockButton from '~/components/Button/BlockUser/Unblock' import { FollowButton } from '~/components/Button/Follow' import { numAbbr, toPath } from '~/common/utils' @@ -49,12 +50,14 @@ const FullDesc = ({ user, nameSize = 'default', readonly, - appreciations + appreciations, + showUnblock }: { user: UserDigestFullDescUser nameSize?: 'default' | 'small' readonly?: boolean appreciations?: number + showUnblock?: boolean }) => { const showAppreciations = appreciations && appreciations > 0 const nameSizeClasses = classNames({ @@ -87,11 +90,12 @@ const FullDesc = ({ {showAppreciations && } - + {!showUnblock && }
- + {showUnblock && } + {!showUnblock && }
@@ -120,10 +124,12 @@ FullDesc.fragments = { ...AvatarUser ...FollowStateUser ...FollowButtonUser + ...UnblockButtonUser } ${Avatar.fragments.user} ${FollowButton.State.fragments.user} ${FollowButton.fragments.user} + ${UnblockButton.fragments.user} ` } diff --git a/src/components/UserProfile/index.tsx b/src/components/UserProfile/index.tsx index 01a797159d..18db4ededc 100644 --- a/src/components/UserProfile/index.tsx +++ b/src/components/UserProfile/index.tsx @@ -174,6 +174,7 @@ const BaseUserProfile = () => { size="xlarge" user={!isMe && viewer.isInactive ? undefined : user} /> + {!isMe && (
@@ -194,6 +195,7 @@ const BaseUserProfile = () => { {!isMe && } )} + {viewer.isArchived && ( { /> )} + {viewer.isFrozen && ( { /> )} + {viewer.isBanned && ( { )}
+
{isMe && !viewer.isInactive && ( )} + {!isMe && } + @@ -233,6 +240,7 @@ const BaseUserProfile = () => { {!viewer.isInactive && ( )} +
@@ -245,6 +253,7 @@ const BaseUserProfile = () => { /> + diff --git a/src/pages/MeSettingsBlocked.tsx b/src/pages/MeSettingsBlocked.tsx new file mode 100644 index 0000000000..10e383a3a4 --- /dev/null +++ b/src/pages/MeSettingsBlocked.tsx @@ -0,0 +1,9 @@ +import MeSettingsBlocked from '~/views/Me/Settings/Blocked' + +import { Protected } from '~/components/Protected' + +export default () => ( + + + +) diff --git a/src/views/Me/Settings/Blocked/SettingsBlocked.tsx b/src/views/Me/Settings/Blocked/SettingsBlocked.tsx new file mode 100644 index 0000000000..8cda48b766 --- /dev/null +++ b/src/views/Me/Settings/Blocked/SettingsBlocked.tsx @@ -0,0 +1,104 @@ +import gql from 'graphql-tag' +import { useQuery } from 'react-apollo' + +import { InfiniteScroll, Spinner, Translate } from '~/components' +import EmptyWarning from '~/components/Empty/EmptyWarning' +import { QueryError } from '~/components/GQL' +import { UserDigest } from '~/components/UserDigest' + +import { ANALYTICS_EVENTS, FEED_TYPE } from '~/common/enums' +import { analytics, mergeConnections } from '~/common/utils' + +import { ViewerBlockList } from './__generated__/ViewerBlockList' + +const VIEWER_BLOCK_LIST = gql` + query ViewerBlockList($after: String) { + viewer { + id + blockList(input: { first: 10, after: $after }) { + pageInfo { + startCursor + endCursor + hasNextPage + } + edges { + cursor + node { + ...UserDigestFullDescUser + } + } + } + } + } + ${UserDigest.FullDesc.fragments.user} +` + +const SettingsBlocked = () => { + const { data, loading, error, fetchMore } = useQuery( + VIEWER_BLOCK_LIST + ) + + if (loading) { + return + } + + if (error) { + return + } + + const connectionPath = 'viewer.blockList' + const { edges, pageInfo } = + (data && data.viewer && data.viewer.blockList) || {} + + const filteredUsers = (edges || []).filter(({ node }) => node.isBlocked) + + if (!edges || edges.length <= 0 || filteredUsers.length <= 0 || !pageInfo) { + return ( + + } + /> + ) + } + + const loadMore = () => { + analytics.trackEvent(ANALYTICS_EVENTS.LOAD_MORE, { + type: FEED_TYPE.ALL_AUTHORS, + location: edges.length + }) + return fetchMore({ + variables: { + after: pageInfo.endCursor + }, + updateQuery: (previousResult, { fetchMoreResult }) => + mergeConnections({ + oldData: previousResult, + newData: fetchMoreResult, + path: connectionPath + }) + }) + } + + return ( + +
    + {filteredUsers.map(({ node, cursor }, i) => ( +
  • + analytics.trackEvent(ANALYTICS_EVENTS.CLICK_FEED, { + type: FEED_TYPE.ALL_AUTHORS, + location: i + }) + } + > + +
  • + ))} +
+
+ ) +} + +export default SettingsBlocked diff --git a/src/views/Me/Settings/Blocked/index.tsx b/src/views/Me/Settings/Blocked/index.tsx new file mode 100644 index 0000000000..fb3fdca1cf --- /dev/null +++ b/src/views/Me/Settings/Blocked/index.tsx @@ -0,0 +1,15 @@ +import SettingsTab from '../SettingsTab' +import SettingsBlocked from './SettingsBlocked' + +export default () => ( +
+
+
+ +
+
+ +
+
+
+) diff --git a/src/views/Me/Settings/SettingsTab/index.tsx b/src/views/Me/Settings/SettingsTab/index.tsx index babcf357c0..0e90eef85d 100644 --- a/src/views/Me/Settings/SettingsTab/index.tsx +++ b/src/views/Me/Settings/SettingsTab/index.tsx @@ -20,6 +20,7 @@ const SettingsTabs = () => {
+ @@ -32,6 +33,17 @@ const SettingsTabs = () => { + + + + + + + + ) } From a1767af51159bae90542c4fa5d2e53744d557072 Mon Sep 17 00:00:00 2001 From: RoberTu Date: Thu, 24 Oct 2019 19:29:52 +0800 Subject: [PATCH 3/7] Add confirm modal for block user; --- src/common/enums/text.ts | 12 ++- .../Button/BlockUser/Dropdown/index.tsx | 101 +++++++++--------- .../Button/BlockUser/Unblock/index.tsx | 4 +- .../CommentDigest/DropdownActions/index.tsx | 98 +++++++---------- src/components/Fingerprint/index.tsx | 15 +-- src/components/GQL/fragments/user.ts | 12 +++ src/components/Modal/BlockUserModal/index.tsx | 99 +++++++++++++++++ .../UserProfile/DropdownActions/index.tsx | 29 +++-- src/components/UserProfile/index.tsx | 2 +- src/views/ArticleDetail/index.tsx | 7 +- .../Me/Settings/Blocked/SettingsBlocked.tsx | 33 +++++- 11 files changed, 266 insertions(+), 146 deletions(-) create mode 100644 src/components/GQL/fragments/user.ts create mode 100644 src/components/Modal/BlockUserModal/index.tsx diff --git a/src/common/enums/text.ts b/src/common/enums/text.ts index ac7fa33856..766587a71b 100644 --- a/src/common/enums/text.ts +++ b/src/common/enums/text.ts @@ -131,8 +131,10 @@ export const TEXT = { articleFingerprint: '作品指紋', copySuccess: '複製成功', copy: '複製', - block: '屏蔽用戶', - unblock: '取消屏蔽', + block: '屏蔽', + blockUser: '屏蔽用戶', + unblockUser: '取消屏蔽', + blockSuccess: '屏蔽成功', pin: '喜歡回應', unpin: '取消精選', emptySearchResults: '沒有找到你搜索的內容', @@ -302,8 +304,10 @@ export const TEXT = { articleFingerprint: '作品指纹', copySuccess: '复制成功', copy: '复制', - block: '屏蔽用户', - unblock: '取消屏蔽', + block: '屏蔽', + blockUser: '屏蔽用户', + unblockUser: '取消屏蔽', + blockSuccess: '屏蔽成功', pin: '喜欢回应', unpin: '取消精选', emptySearchResults: '没有找到你搜寻的内容', diff --git a/src/components/Button/BlockUser/Dropdown/index.tsx b/src/components/Button/BlockUser/Dropdown/index.tsx index 06032315d0..5ee34a2575 100644 --- a/src/components/Button/BlockUser/Dropdown/index.tsx +++ b/src/components/Button/BlockUser/Dropdown/index.tsx @@ -1,26 +1,20 @@ -import gql from 'graphql-tag' - import { Icon, TextIcon, Translate } from '~/components' import { useMutation } from '~/components/GQL' -import { BlockUser } from '~/components/GQL/mutations/__generated__/BlockUser' +import { BlockUser } from '~/components/GQL/fragments/__generated__/BlockUser' +import userFragments from '~/components/GQL/fragments/user' import { UnblockUser } from '~/components/GQL/mutations/__generated__/UnblockUser' -import BLOCK_USER from '~/components/GQL/mutations/blockUser' import UNBLOCK_USER from '~/components/GQL/mutations/unblockUser' +import BlocKUserModal from '~/components/Modal/BlockUserModal' +import { ModalInstance, ModalSwitch } from '~/components/ModalManager' import { TEXT } from '~/common/enums' import ICON_BLOCK from '~/static/icons/block.svg?sprite' import ICON_UNBLOCK from '~/static/icons/unblock.svg?sprite' -import { BlockButtonUser } from './__generated__/BlockButtonUser' import styles from './styles.css' const fragments = { - user: gql` - fragment BlockButtonUser on User { - id - isBlocked - } - ` + user: userFragments.block } const TextIconBlock = () => ( @@ -28,7 +22,7 @@ const TextIconBlock = () => ( icon={} spacing="tight" > - + ) @@ -39,27 +33,22 @@ const TextIconUnblock = () => ( } spacing="tight" > - + ) const BlockUserButton = ({ user, + isShown, hideDropdown }: { - user: BlockButtonUser + user: BlockUser + isShown: boolean hideDropdown: () => void }) => { - const [blockUser] = useMutation(BLOCK_USER, { - variables: { id: user.id }, - optimisticResponse: { - blockUser: { - id: user.id, - isBlocked: true, - __typename: 'User' - } - } - }) const [unblockUser] = useMutation(UNBLOCK_USER, { variables: { id: user.id }, optimisticResponse: { @@ -71,34 +60,48 @@ const BlockUserButton = ({ } }) - if (user.isBlocked) { - return ( - - ) - } + + + )} - return ( - + )} + + )} - - + {isShown && ( + + {(props: ModalInstanceProps) => ( + + )} + + )} + ) } diff --git a/src/components/Button/BlockUser/Unblock/index.tsx b/src/components/Button/BlockUser/Unblock/index.tsx index e258f16620..51d7b1284f 100644 --- a/src/components/Button/BlockUser/Unblock/index.tsx +++ b/src/components/Button/BlockUser/Unblock/index.tsx @@ -50,8 +50,8 @@ const Unblock = ({ bgColor="green" > ) diff --git a/src/components/CommentDigest/DropdownActions/index.tsx b/src/components/CommentDigest/DropdownActions/index.tsx index 9b45f3fc20..7a5b4a083a 100644 --- a/src/components/CommentDigest/DropdownActions/index.tsx +++ b/src/components/CommentDigest/DropdownActions/index.tsx @@ -20,7 +20,7 @@ const fragments = { state author { id - ...BlockButtonUser + ...BlockUser } parentComment { id @@ -39,54 +39,6 @@ const fragments = { ` } -const DropdownContent: React.FC<{ - comment: DropdownActionsComment - hideDropdown: () => void - editComment?: () => void - isShowPinButton: boolean - isShowEditButton: boolean - isShowDeleteButton: boolean - isShowBlockUserButton: boolean -}> = ({ - comment, - editComment, - hideDropdown, - isShowPinButton, - isShowEditButton, - isShowDeleteButton, - isShowBlockUserButton -}) => { - return ( - - {isShowPinButton && ( - - - - )} - {isShowEditButton && editComment && ( - - - - )} - {/* {!isCommentAuthor && isActive && ( - - - - )} */} - {isShowDeleteButton && ( - - - - )} - {isShowBlockUserButton && ( - - - - )} - - ) -} - const DropdownActions = ({ comment, editComment @@ -94,6 +46,7 @@ const DropdownActions = ({ comment: DropdownActionsComment editComment?: () => void }) => { + const [shown, setShown] = useState(false) const [instance, setInstance] = useState(null) const hideDropdown = () => { if (!instance) { @@ -129,18 +82,47 @@ const DropdownActions = ({ return ( + + {isShowPinButton && ( + + + + )} + {isShowEditButton && editComment && ( + + + + )} + {/* {!isCommentAuthor && isActive && ( + + + + )} */} + {isShowDeleteButton && ( + + + + )} + {isShowBlockUserButton && ( + + + + )} + } trigger="click" onCreate={setInstance} + onShown={() => setShown(true)} placement="bottom-end" zIndex={301} > diff --git a/src/components/Fingerprint/index.tsx b/src/components/Fingerprint/index.tsx index 381d07e2dd..6ff4373153 100644 --- a/src/components/Fingerprint/index.tsx +++ b/src/components/Fingerprint/index.tsx @@ -29,13 +29,7 @@ const GATEWAYS = gql` } ` -const FingerprintContent = ({ - shown, - dataHash -}: { - shown: boolean - dataHash: string -}) => { +const FingerprintContent = ({ dataHash }: { dataHash: string }) => { const [gatewaysExpand, setGatewaysExpand] = useState(false) const [helpExpand, setHelpExpand] = useState(false) @@ -202,16 +196,11 @@ const Fingerprint = ({ color?: 'grey' | 'green' size?: 'xs' | 'sm' }) => { - const [shown, setShown] = useState(false) - return ( setShown(true)} - content={ - - } + content={} > + + + {(open: any) => ( + + )} + )} diff --git a/src/components/Form/CommentForm/index.tsx b/src/components/Form/CommentForm/index.tsx index abd6abbcf2..d43f6876dc 100644 --- a/src/components/Form/CommentForm/index.tsx +++ b/src/components/Form/CommentForm/index.tsx @@ -9,6 +9,7 @@ import COMMENT_COMMENTS from '~/components/GQL/queries/commentComments' import { Icon } from '~/components/Icon' import IconSpinner from '~/components/Icon/Spinner' import { Translate } from '~/components/Language' +import { ModalSwitch } from '~/components/ModalManager' import { Spinner } from '~/components/Spinner' import { ViewerContext } from '~/components/Viewer' @@ -193,6 +194,25 @@ const CommentForm = ({ } export default (props: CommentFormProps) => { + const viewer = useContext(ViewerContext) + + if (viewer.isOnboarding || !viewer.likerId) { + return ( + + {(open: any) => ( + + )} + + ) + } + if (props.blocked) { return (
diff --git a/src/components/Form/CommentForm/styles.css b/src/components/Form/CommentForm/styles.css index ff7f5562ab..47ccb77285 100644 --- a/src/components/Form/CommentForm/styles.css +++ b/src/components/Form/CommentForm/styles.css @@ -15,6 +15,8 @@ } .blocked { + display: block; + width: 100%; padding: var(--spacing-x-tight); color: var(--color-grey-dark);