diff --git a/docs/js/graphql.md b/docs/js/graphql.md index 43dcf9752..e49562cce 100755 --- a/docs/js/graphql.md +++ b/docs/js/graphql.md @@ -30,7 +30,7 @@ const pagedCommunities = gql`         contributesDigest         subscribersCount       } -      ${F.pagedCounts} +      ${F.pagi}     }   } ` diff --git a/docs/js/graphql.zh-CN.md b/docs/js/graphql.zh-CN.md index ff228c6b0..69586b268 100755 --- a/docs/js/graphql.zh-CN.md +++ b/docs/js/graphql.zh-CN.md @@ -30,7 +30,7 @@ const pagedCommunities = gql` contributesDigest subscribersCount } - ${F.pagedCounts} + ${F.pagi} } } ` diff --git a/server/routes.js b/server/routes.js index d4444b68d..f4637363d 100644 --- a/server/routes.js +++ b/server/routes.js @@ -79,8 +79,15 @@ router.route('/works/:id').get((req, res) => { }) // 用户页 -router.route('/user/:userId').get((req, res) => { - return renderAndCache({ req, res, path: '/user' }) +router.route('/u/:login').get((req, res) => { + const { login } = req.params + return renderAndCache({ req, res, path: `/user/${login}` }) +}) + +router.route('/user/:login').get((req, res) => { + const { login } = req.params + console.log('hello user ?: ', login) + return renderAndCache({ req, res, path: `/user/${login}` }) }) // 帖子页 diff --git a/src/containers/content/ExploreContent/schema.ts b/src/containers/content/ExploreContent/schema.ts index f24e801ad..6f30588f9 100755 --- a/src/containers/content/ExploreContent/schema.ts +++ b/src/containers/content/ExploreContent/schema.ts @@ -10,7 +10,7 @@ const pagedCommunities = gql` subscribersCount viewerHasSubscribed @include(if: $userHasLogin) } - ${F.pagedCounts} + ${F.pagi} } } ` @@ -24,7 +24,7 @@ const searchCommunities = gql` subscribersCount viewerHasSubscribed @include(if: $userHasLogin) } - ${F.pagedCounts} + ${F.pagi} } } ` @@ -42,14 +42,14 @@ const subscribeCommunity = gql` } ` const unsubscribeCommunity = gql` - mutation($communityId: ID!) { + mutation ($communityId: ID!) { unsubscribeCommunity(communityId: $communityId) { id } } ` const pagedCategories = gql` - query($filter: PagedFilter!) { + query ($filter: PagedFilter!) { pagedCategories(filter: $filter) { entries { id diff --git a/src/containers/content/UserContent/AchieveCard.js b/src/containers/content/UserContent/AchieveCard.js deleted file mode 100755 index 7fff3afed..000000000 --- a/src/containers/content/UserContent/AchieveCard.js +++ /dev/null @@ -1,30 +0,0 @@ -import React from 'react' - -import FocusLine from '@/widgets/FocusLine' -import { ICON_CMD } from '@/config' - -// import CommunityEditorInfo from './CommunityEditorInfo' - -import { Wrapper, Title } from './styles/achieve_card' - -const AchieveCard = ({ user }) => ( - - 个人成就 - - - - {/* */} - -) - -export default React.memo(AchieveCard) diff --git a/src/containers/content/UserContent/DigestBoard.js b/src/containers/content/UserContent/DigestBoard.js deleted file mode 100755 index 3ec8b05f4..000000000 --- a/src/containers/content/UserContent/DigestBoard.js +++ /dev/null @@ -1,74 +0,0 @@ -import React from 'react' -import TimeAgo from 'timeago-react' -import { any, equals, values } from 'ramda' - -import { ICON_CMD } from '@/config' - -import DotDivider from '@/widgets/DotDivider' -import FollowButton from '@/widgets/Buttons/FollowButton' -import { Space } from '@/widgets/Common' - -import AchieveCard from './AchieveCard' -import SourceContributeInfo from './SourceContributeInfo' - -import { CardWrapper, AttachWrapper, AttachIcon } from './styles/digest_board' - -import * as logic from './logic' - -const anyTrue = (obj) => any(equals(true), values(obj)) - -const DigestBoard = ({ user, accountId, following }) => ( - <> - - - {user.id !== accountId && ( - - )} - - - - 第 {user.id} 位会员{' '} - 加入时间: - - - - - - - 主页被浏览 {user.views} 次 - - - {anyTrue(user.achievement.sourceContribute) && ( - - )} - - {user.achievement.donateMember && !user.achievement.seniorMember && ( - - -
热心打赏
-
- )} - - {user.achievement.seniorMember && ( - - -
CPS 会员
-
- )} - {user.achievement.sponsorMember && ( - - -
特别赞助
-
- )} - -) - -export default React.memo(DigestBoard) diff --git a/src/containers/content/UserContent/Sidebar/index.tsx b/src/containers/content/UserContent/Sidebar/index.tsx index ad29912f9..667ee1517 100644 --- a/src/containers/content/UserContent/Sidebar/index.tsx +++ b/src/containers/content/UserContent/Sidebar/index.tsx @@ -1,19 +1,29 @@ import { FC, memo } from 'react' -import type { TUser } from '@/spec' -import { VIEW } from '@/constant' +import type { TUser, TPagedWorks, TPagedCommunities } from '@/spec' import UserBrief from '@/widgets/UserBrief' import { Wrapper } from '../styles/sidebar' type TProps = { - viewingUser: TUser + user: TUser isSelfViewing?: boolean + works: TPagedWorks + editableCommunities: TPagedCommunities } -const Sidebar: FC = ({ viewingUser, isSelfViewing = false }) => { +const Sidebar: FC = ({ + user, + works, + editableCommunities, + isSelfViewing = false, +}) => { return ( - + ) } diff --git a/src/containers/content/UserContent/SourceContributeInfo.js b/src/containers/content/UserContent/SourceContributeInfo.js deleted file mode 100755 index 7682a07a0..000000000 --- a/src/containers/content/UserContent/SourceContributeInfo.js +++ /dev/null @@ -1,37 +0,0 @@ -import React from 'react' - -import { ICON_CMD, GITHUB_WEB_ADDR, GITHUB_SERVER_ADDR } from '@/config' - -import { AttachWrapper, AttachIcon, AttachLink } from './styles/digest_board' -import { Split } from './styles/source_contribute_info' - -// TODO: link_to coderplanets_web/commits?author=mydearxym - -const SourceContributeInfo = ({ data }) => ( - - - 本站源码贡献者( - {data.web && ( - - web - - )} - {data.web && data.server && ,} - {data.server && ( - - server - - )} - ) - -) - -export default React.memo(SourceContributeInfo) diff --git a/src/containers/content/UserContent/index.tsx b/src/containers/content/UserContent/index.tsx index 9c40b17e7..7e5364112 100755 --- a/src/containers/content/UserContent/index.tsx +++ b/src/containers/content/UserContent/index.tsx @@ -11,19 +11,16 @@ import { USER_THREAD } from '@/constant' import { buildLog } from '@/utils/logger' import { pluggedIn } from '@/utils/mobx' +import { Comments } from '@/containers/dynamic' import UserProfile from '@/containers/user/UserProfile' -import UserPublished from '@/containers/user/UserPublished' -import UserPublishedComments from '@/containers/user/UserPublishedComments' -import UserBilling from '@/containers/user/UserBilling' +import UserPublishedArticles from '@/containers/user/UserPublishedArticles' +// import UserBilling from '@/containers/user/UserBilling' import UserSettings from '@/containers/user/UserSettings' -import UserStared from '@/containers/user/UserStared' -import UserFavorited from '@/containers/user/UserFavorited' import TabBar from '@/widgets/TabBar' import type { TStore } from './store' import Sidebar from './Sidebar' -// import DigestBoard from './DigestBoard' import { Wrapper, @@ -31,7 +28,7 @@ import { BannerWrapper, ContentWrapper, TabBarWrapper, - // MobileBottom, + PublishedCommentsWrapper, } from './styles' import { useInit, tabOnChange } from './logic' @@ -52,22 +49,22 @@ const BaseTaberThreads = [ title: '讨论', raw: 'comments', }, - { - title: '收藏', - raw: 'favorites', - }, - { - title: '喜欢', - raw: 'likes', - }, + // { + // title: '收藏', + // raw: 'favorites', + // }, + // { + // title: '喜欢', + // raw: 'likes', + // }, ] const FullTaberThreads = [ ...BaseTaberThreads, - { - title: '账单', - raw: 'billing', - }, + // { + // title: '账单', + // raw: 'billing', + // }, { title: '设置', raw: 'settings', @@ -80,22 +77,20 @@ const TabberContent = ({ active }) => { return case USER_THREAD.COMMENTS: - return - - case USER_THREAD.FAVORITES: - return + return ( + + + + ) - case USER_THREAD.LINKS: - return - - case USER_THREAD.BILLING: - return + // case USER_THREAD.BILLING: + // return case USER_THREAD.SETTINGS: return default: - return + return } } @@ -110,33 +105,32 @@ const UserContentContainer: FC = ({ userContent: store, metric }) => { const { activeThread, viewingUser, - // accountInfo, isSelfViewing, - // following, + pagedWorksData, + pagedEditableCommunitiesData, + hasContentBg, } = store - const taberSource = isSelfViewing ? FullTaberThreads : BaseTaberThreads return ( - - - - - + - - + + + + + - {/* */} diff --git a/src/containers/content/UserContent/logic.ts b/src/containers/content/UserContent/logic.ts index 3abcc206d..0e94a6fdb 100755 --- a/src/containers/content/UserContent/logic.ts +++ b/src/containers/content/UserContent/logic.ts @@ -71,6 +71,13 @@ export const tabOnChange = (activeThread: TThread): void => { // Data & Error handlers // ############################### +const loadEditableCommunities = () => { + const { login } = store.viewingUser + const filter = { page: 1, size: 7 } + + sr71$.query(S.editableCommunities, { login, filter }) +} + const DataSolver = [ { match: asyncRes('follow'), @@ -80,6 +87,12 @@ const DataSolver = [ match: asyncRes('undoFollow'), action: () => getUserFollowStates(), }, + { + match: asyncRes('editableCommunities'), + action: ({ editableCommunities }) => { + store.mark({ pagedEditableCommunities: editableCommunities }) + }, + }, { match: asyncRes('user'), action: ({ user }) => { @@ -120,6 +133,8 @@ export const useInit = (_store: TStore): void => { store = _store // log('effect init') sub$ = sr71$.data().subscribe($solver(DataSolver, ErrSolver)) + // TODO: query before judage + loadEditableCommunities() return () => { if (!sub$) return diff --git a/src/containers/content/UserContent/schema.ts b/src/containers/content/UserContent/schema.ts index f883340a7..6e9d098d9 100755 --- a/src/containers/content/UserContent/schema.ts +++ b/src/containers/content/UserContent/schema.ts @@ -1,14 +1,15 @@ import { gql } from '@urql/core' +import { F } from '@/schemas' const follow = gql` - mutation($userId: ID!) { + mutation ($userId: ID!) { follow(userId: $userId) { id } } ` const undoFollow = gql` - mutation($userId: ID!) { + mutation ($userId: ID!) { undoFollow(userId: $userId) { id } @@ -24,10 +25,22 @@ const user = gql` } ` +const editableCommunities = gql` + query ($login: String, $filter: PagedFilter!) { + editableCommunities(login: $login, filter: $filter) { + entries { + ${F.community} + } + ${F.pagi} + } + } +` + const schema = { follow, undoFollow, user, + editableCommunities, } export default schema diff --git a/src/containers/content/UserContent/store.ts b/src/containers/content/UserContent/store.ts index dace7ffe5..9ee7dde28 100755 --- a/src/containers/content/UserContent/store.ts +++ b/src/containers/content/UserContent/store.ts @@ -6,9 +6,16 @@ import { types as T, getParent, Instance } from 'mobx-state-tree' import { values } from 'ramda' -import type { TRootStore, TAccount, TUser } from '@/spec' +import type { + TRootStore, + TAccount, + TUser, + TPagedWorks, + TPagedCommunities, +} from '@/spec' import { USER_THREAD } from '@/constant' import { markStates, toJS } from '@/utils/mobx' +import { PagedWorks, PagedCommunities, emptyPagi } from '@/model' const UserContent = T.model('UserContent', { activeThread: T.optional( @@ -16,6 +23,9 @@ const UserContent = T.model('UserContent', { USER_THREAD.PROFILE, ), following: T.optional(T.boolean, false), + + pagedEditableCommunities: T.optional(PagedCommunities, emptyPagi), + pagedWorks: T.optional(PagedWorks, emptyPagi), }) .views((self) => ({ get isLogin(): boolean { @@ -34,6 +44,21 @@ const UserContent = T.model('UserContent', { const root = getParent(self) as TRootStore return root.viewing.isSelfViewing }, + get pagedWorksData(): TPagedWorks { + return toJS(self.pagedWorks) + }, + get pagedEditableCommunitiesData(): TPagedCommunities { + return toJS(self.pagedEditableCommunities) + }, + get hasContentBg(): boolean { + const root = getParent(self) as TRootStore + + if (self.activeThread === USER_THREAD.PUBLISH) { + return root.userPublishedArticles.hasContentBg + } + + return true + }, })) .actions((self) => ({ authWarning(options) { diff --git a/src/containers/content/UserContent/styles/digest_board.ts b/src/containers/content/UserContent/styles/digest_board.ts deleted file mode 100755 index 7a01bd3d2..000000000 --- a/src/containers/content/UserContent/styles/digest_board.ts +++ /dev/null @@ -1,36 +0,0 @@ -import styled from 'styled-components' - -import Img from '@/Img' -import { theme } from '@/utils/themes' -import css from '@/utils/css' - -export const CardWrapper = styled.div` - box-shadow: 0 1px 4px rgba(0, 0, 0, 0.04); - padding: 20px; - padding-bottom: 10px; - min-height: 100px; - margin-bottom: 15px; -` -export const AttachWrapper = styled.div` - ${css.flex('align-center')}; - font-size: 0.8rem; - color: ${theme('banner.desc')}; - margin-left: 10px; - margin-bottom: 4px; -` -export const AttachIcon = styled(Img)` - fill: ${theme('banner.desc')}; - ${css.size(15)}; - margin-right: 5px; -` -export const AttachLink = styled.a` - text-decoration: underline; - font-weight: bolder; - transition: color 0.3s; - color: ${theme('banner.desc')}; - - &:hover { - color: ${theme('banner.title')}; - cursor: pointer; - } -` diff --git a/src/containers/content/UserContent/styles/index.ts b/src/containers/content/UserContent/styles/index.ts index 0908d7697..4540722fb 100755 --- a/src/containers/content/UserContent/styles/index.ts +++ b/src/containers/content/UserContent/styles/index.ts @@ -22,17 +22,20 @@ export const BannerWrapper = styled.div<{ metric: TMetric }>` margin-bottom: 20px; ${({ metric }) => css.fitPageWidth(metric)}; ` -export const ContentWrapper = styled.div` +export const ContentWrapper = styled.div<{ hasContentBg: boolean }>` + position: relative; flex-grow: 1; min-height: 600px; padding: 20px; padding-top: 15px; - background: ${theme('thread.bg')}; + + background: ${({ hasContentBg }) => + hasContentBg ? theme('thread.bg') : 'transparent'}; ` export const TabBarWrapper = styled.div` position: absolute; - bottom: 0; - left: 380px; + top: -66px; + left: 15px; width: 100%; ` export const MobileBottom = styled.div` @@ -41,3 +44,9 @@ export const MobileBottom = styled.div` display: none; ${css.media.tablet`display: block`}; ` + +export const PublishedCommentsWrapper = styled.div` + margin-top: -20px; + padding-left: 8px; + padding-right: 20px; +` diff --git a/src/containers/content/UserContent/styles/sidebar/index.ts b/src/containers/content/UserContent/styles/sidebar/index.ts index b568449a4..21dba8d90 100644 --- a/src/containers/content/UserContent/styles/sidebar/index.ts +++ b/src/containers/content/UserContent/styles/sidebar/index.ts @@ -8,6 +8,7 @@ export const Wrapper = styled.div` position: relative; ${css.flexColumn('align-center')}; width: 300px; + min-width: 300px; padding-right: 40px; padding-left: 30px; margin-top: -110px; diff --git a/src/containers/content/UserContent/styles/source_contribute_info.ts b/src/containers/content/UserContent/styles/source_contribute_info.ts deleted file mode 100755 index 90aa6fe9a..000000000 --- a/src/containers/content/UserContent/styles/source_contribute_info.ts +++ /dev/null @@ -1,8 +0,0 @@ -import styled from 'styled-components' - -export const Wrapper = styled.div` - display: flex; -` -export const Split = styled.span` - margin-right: 3px; -` diff --git a/src/containers/dynamic.tsx b/src/containers/dynamic/index.tsx similarity index 52% rename from src/containers/dynamic.tsx rename to src/containers/dynamic/index.tsx index 5e2cd10ca..82ef85908 100644 --- a/src/containers/dynamic.tsx +++ b/src/containers/dynamic/index.tsx @@ -1,16 +1,16 @@ import dynamic from 'next/dynamic' import { LavaLampLoading } from '@/widgets/dynamic' -export const ArticleFooter = dynamic(() => import('./unit/ArticleFooter'), { +export const ArticleFooter = dynamic(() => import('../unit/ArticleFooter'), { ssr: false, }) -export const Comments = dynamic(() => import('./unit/Comments'), { +export const Comments = dynamic(() => import('../unit/Comments'), { /* eslint-disable react/display-name */ loading: () => , ssr: false, }) -export const Cashier = dynamic(() => import('./tool/Cashier'), { +export const Cashier = dynamic(() => import('../tool/Cashier'), { ssr: false, }) diff --git a/src/containers/editor/AccountEditor/EducationBackgroundInputer.js b/src/containers/editor/AccountEditor/EducationBackgroundInputer.js deleted file mode 100755 index 1eca22b13..000000000 --- a/src/containers/editor/AccountEditor/EducationBackgroundInputer.js +++ /dev/null @@ -1,89 +0,0 @@ -import React from 'react' - -import FormItem from '@/widgets/FormItem' -import Maybe from '@/widgets/Maybe' -import { Space } from '@/widgets/Common' -import { ICON_CMD } from '@/config' - -import { - Wrapper, - Adder, - FormItemWrapper, - FormLabel, - FormInput, - BackgroundsWrapper, - BackgroundItem, - BgTitle, - BgDivider, - BgDesc, - DeleteIcon, -} from './styles/background_inputer' - -import { addBackground, removeEduBackground, updateBackground } from './logic' - -const FormBar = ({ label, data, ratKey, mainChange, subChange }) => ( - - {label} - - - - - -
- -
-
-
-) - -const BackgroundList = ({ list }) => ( - - - {list.map((item) => ( - - {item.school} - - - <> - · - {item.major} - - - -
- -
-
- ))} -
-
-) - -const EducationBackgroundInputer = ({ user, ratKey, data }) => ( - - - - -) - -export default React.memo(EducationBackgroundInputer) diff --git a/src/containers/editor/AccountEditor/SexInputer.js b/src/containers/editor/AccountEditor/SexInputer.js deleted file mode 100755 index b55ec5a44..000000000 --- a/src/containers/editor/AccountEditor/SexInputer.js +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react' -import T from 'prop-types' - -import { ICON_CMD } from '@/config' - -import { - Wrapper, - SexLable, - SexInput, - Dude, - Girl, - DudeIcon, - GirlIcon, -} from './styles/sex_inputer' - -import { sexChange } from './logic' - -const SexInputer = ({ label, value }) => ( - - {label} - - - - - - - - - -) - -SexInputer.propTypes = { - value: T.string.isRequired, - label: T.string, -} - -SexInputer.defaultProps = { - label: '性别:', -} - -export default React.memo(SexInputer) diff --git a/src/containers/editor/AccountEditor/SexInputer.tsx b/src/containers/editor/AccountEditor/SexInputer.tsx new file mode 100755 index 000000000..6cccaad7f --- /dev/null +++ b/src/containers/editor/AccountEditor/SexInputer.tsx @@ -0,0 +1,21 @@ +import { FC, memo } from 'react' + +import { Wrapper, DudeIcon, GirlIcon } from './styles/sex_inputer' + +import { SEX } from './constant' +import { inputOnChange } from './logic' + +type TProps = { + value: string +} + +const SexInputer: FC = ({ value }) => { + return ( + + inputOnChange(SEX.DUDE, 'sex')} /> + inputOnChange(SEX.GIRL, 'sex')} /> + + ) +} + +export default memo(SexInputer) diff --git a/src/containers/editor/AccountEditor/SocialInputer.js b/src/containers/editor/AccountEditor/SocialInputer.js deleted file mode 100755 index 49a3a5caf..000000000 --- a/src/containers/editor/AccountEditor/SocialInputer.js +++ /dev/null @@ -1,104 +0,0 @@ -import React from 'react' - -import { ICON_CMD } from '@/config' -import { buildLog, nilOrEmpty, SOCIAL_LISTS } from '@/utils' - -import Input from '@/widgets/Input' - -import { - Wrapper, - InputWrapper, - SocialIconsWrapper, - SocialIcon, - FormItemWrapper, - FormLabel, - FormInput, - UpIcon, - DownIcon, - TogglerLabelWrapper, - TogglerTextWrapper, - TogglerDivider, - TogglerLabelText, - TogglerText, -} from './styles/social_inputer' - -import { toggleSocials, socialOnChange } from './logic' - -/* eslint-disable-next-line */ -const log = buildLog('C:AccountEditor') - -const SocialItem = ({ label, prefix, value, icon, onChange }) => ( - - {label} - - - - - -) - -const SocialIconList = ({ show, user }) => ( - - 社交账号: - - - {SOCIAL_LISTS.map((social) => ( - - ))} - - - {show ? ( - <> - ... 收起 - - - ) : ( - <> - ... 编辑 - - - )} - - - -) - -const SocialInputer = ({ show, user }) => ( - - - - {/* eslint-disable react/jsx-key */} - {/* set key to SocialItem will cause input lose focus */} - {SOCIAL_LISTS.map((social) => ( - - ))} - {/* eslint-enable react/jsx-key */} - - - - 收起社交信息 - - - - - -) - -export default React.memo(SocialInputer) diff --git a/src/containers/editor/AccountEditor/SocialInputer.tsx b/src/containers/editor/AccountEditor/SocialInputer.tsx new file mode 100755 index 000000000..e87598d99 --- /dev/null +++ b/src/containers/editor/AccountEditor/SocialInputer.tsx @@ -0,0 +1,79 @@ +/* + * + * AccountEditor + * + */ + +import { FC, memo, Fragment } from 'react' + +import { buildLog } from '@/utils/logger' + +import type { TEditData } from './spec' +import { Input, Section, ICON } from './styles/social_inputer' +import { inputOnChange } from './logic' + +/* eslint-disable-next-line */ +const log = buildLog('C:AccountEditor') + +type TProps = { + editData?: TEditData +} + +const SocialInputer: FC = ({ editData }) => { + const { profile, social } = editData + return ( + +
+ + inputOnChange(e, 'location')} + /> +
+
+ + inputOnChange(e, 'company')} + /> +
+
+ + inputOnChange(e, 'blog')} + /> +
+ +
+ + inputOnChange(e, 'github')} + /> +
+
+ + inputOnChange(e, 'twitter')} + /> +
+
+ + inputOnChange(e, 'email')} + /> +
+
+ ) +} + +export default memo(SocialInputer) diff --git a/src/containers/editor/AccountEditor/WorkBackgroundInputer.js b/src/containers/editor/AccountEditor/WorkBackgroundInputer.js deleted file mode 100755 index a038bea7b..000000000 --- a/src/containers/editor/AccountEditor/WorkBackgroundInputer.js +++ /dev/null @@ -1,89 +0,0 @@ -import React from 'react' - -import { ICON_CMD } from '@/config' - -import FormItem from '@/widgets/FormItem' -import Maybe from '@/widgets/Maybe' -import { Space } from '@/widgets/Common' - -import { - Wrapper, - Adder, - FormItemWrapper, - FormLabel, - FormInput, - BackgroundsWrapper, - BackgroundItem, - BgTitle, - BgDivider, - BgDesc, - DeleteIcon, -} from './styles/background_inputer' - -import { addBackground, removeWorkBackground, updateBackground } from './logic' -// import * as logic from './logic' - -const FormBar = ({ label, data, ratKey, mainChange, subChange }) => ( - - {label} - - - - - -
- -
-
-
-) - -const BackgroundList = ({ list }) => ( - - - {list.map((item) => ( - - {item.company} - - <> - · - {item.title} - - -
- -
-
- ))} -
-
-) - -const WorkBackgroundInputer = ({ user, ratKey, data }) => ( - - - - -) - -export default React.memo(WorkBackgroundInputer) diff --git a/src/containers/editor/AccountEditor/constant.ts b/src/containers/editor/AccountEditor/constant.ts new file mode 100644 index 000000000..44b6e0d28 --- /dev/null +++ b/src/containers/editor/AccountEditor/constant.ts @@ -0,0 +1,6 @@ +export const SEX = { + DUDE: 'dude', + GIRL: 'girl', +} + +export const holder = 1 diff --git a/src/containers/editor/AccountEditor/index.js b/src/containers/editor/AccountEditor/index.js deleted file mode 100755 index b853a0ff6..000000000 --- a/src/containers/editor/AccountEditor/index.js +++ /dev/null @@ -1,126 +0,0 @@ -/* - * - * AccountEditor - * - */ - -import React from 'react' - -import { ICON_CMD } from '@/config' -import { buildLog } from '@/utils/logger' -import { pluggedIn } from '@/utils/mobx' - -import Button from '@/widgets/Buttons/Button' -import StatusBox from '@/widgets/StatusBox' -import FormItem from '@/widgets/FormItem' -import WorkBackgroundInputer from './WorkBackgroundInputer' -import EducationBackgroundInputer from './EducationBackgroundInputer' -import SocialInputer from './SocialInputer' -import SexInputer from './SexInputer' - -import { - Wrapper, - BackIcon, - AvatarPic, - FormsWrapper, - Divider, - ActionBtns, -} from './styles' - -import { - useInit, - goBack, - inputOnChange, - cancelEdit, - updateConfirm, -} from './logic' - -/* eslint-disable-next-line */ -const log = buildLog('C:AccountEditor') - -const AccountEditorContainer = ({ accountEditor: store }) => { - useInit(store) - - const { - showSocials, - editUserData, - educationBgData, - workBgData, - updating, - success, - error, - warn, - statusMsg, - ratKey, - } = store - - return ( - -
- -
- - {editUserData.avatar && } - - - - - - - - - {editUserData.sex && } - - - - - - - -        - {updating ? ( - - ) : ( - - )} - - -
- ) -} - -export default pluggedIn(AccountEditorContainer) diff --git a/src/containers/editor/AccountEditor/index.tsx b/src/containers/editor/AccountEditor/index.tsx new file mode 100755 index 000000000..7dafc106f --- /dev/null +++ b/src/containers/editor/AccountEditor/index.tsx @@ -0,0 +1,116 @@ +/* + * + * AccountEditor + * + */ + +import { FC } from 'react' + +import { buildLog } from '@/utils/logger' +import { pluggedIn } from '@/utils/mobx' +import { closeDrawer } from '@/utils/helper' + +import SubmitButton from '@/widgets/Buttons/SubmitButton' +import Tooltip from '@/widgets/Tooltip' +import { Divider, SpaceGrow } from '@/widgets/Common' + +import SexInputer from './SexInputer' +import SocialInputer from './SocialInputer' + +import type { TStore } from './store' + +import { + Wrapper, + AvatarPic, + FormsWrapper, + Section, + RowSection, + LoginSection, + LoginDesc, + GithubIcon, + Label, + SexLabel, + Input, + TextareaInput, + Footer, +} from './styles' + +import { useInit, inputOnChange, onUpdate } from './logic' + +/* eslint-disable-next-line */ +const log = buildLog('C:AccountEditor') + +type TProps = { + accountEditor?: TStore +} + +const AccountEditorContainer: FC = ({ accountEditor: store }) => { + useInit(store) + + const { login, fromGithub, submitState, editData } = store + const { profile } = editData + + return ( + + {profile.avatar && } + + + + {login} + + {fromGithub && ( + + + + )} + +
+ + inputOnChange(e, 'nickname')} + /> +
+ +
+ + inputOnChange(e, 'shortbio')} + /> +
+ + + 性别 + + + +
+ + inputOnChange(e, 'bio')} + /> +
+ + + +
+ +
+
+
+ ) +} + +export default pluggedIn(AccountEditorContainer) as FC diff --git a/src/containers/editor/AccountEditor/logic.js b/src/containers/editor/AccountEditor/logic.js deleted file mode 100755 index 402a216de..000000000 --- a/src/containers/editor/AccountEditor/logic.js +++ /dev/null @@ -1,153 +0,0 @@ -import { useEffect } from 'react' -import { curry, isEmpty, clone, omit, reject, equals, merge } from 'ramda' - -import { EVENT, ERR } from '@/constant' -import { - buildLog, - asyncSuit, - send, - cast, - meteorState, - updateEditing, - errRescue, - nilOrEmpty, -} from '@/utils' - -import { S, updateFields } from './schema' - -/* eslint-disable-next-line */ -const log = buildLog('L:AccountEditor') - -const { SR71, $solver, asyncRes, asyncErr } = asyncSuit -const sr71$ = new SR71() - -let store = null -let sub$ = null - -export const goBack = () => console.log('TODO: close drawer') - -export const inputOnChange = curry((part, e) => updateEditing(store, part, e)) -/* eslint-disable no-unused-vars */ -export const sexChange = curry((sex, e) => store.updateEditing({ sex })) - -/* eslint-disable no-unused-vars */ -export const socialOnChange = curry((part, e) => { - const { editUserData: editUser } = store - editUser.social[part] = e.target.value - - store.mark({ editUser }) -}) - -export const updateBackground = curry((key, part, { target: { value } }) => - store.mark({ [key]: merge(store[key], { [part]: value }) }), -) - -/* eslint-disable no-unused-vars */ -export const addBackground = curry((type, e) => store.addBackground(type)) - -/* eslint-disable no-unused-vars */ -export const removeWorkBackground = curry((company, title, e) => { - const { editUserData } = store - const { workBackgrounds } = editUserData - const newWorkBackgrounds = reject(equals({ company, title }), workBackgrounds) - store.updateEditing({ workBackgrounds: newWorkBackgrounds }) -}) - -/* eslint-disable no-unused-vars */ -export const removeEduBackground = curry((school, major, e) => { - const { editUserData } = store - const { educationBackgrounds } = editUserData - const newEducationBackgrounds = reject( - equals({ school, major }), - educationBackgrounds, - ) - store.updateEditing({ educationBackgrounds: newEducationBackgrounds }) -}) - -export const updateConfirm = () => { - if (!store.statusClean) return false - let profile = cast(updateFields, store.editUserData) - - const educationBackgrounds = clone(profile.educationBackgrounds) - const workBackgrounds = clone(profile.workBackgrounds) - const social = reject(nilOrEmpty, clone(profile.social)) - - profile = omit(['educationBackgrounds', 'workBackgrounds', 'social'], profile) - - const args = { profile } - - if (!isEmpty(educationBackgrounds)) { - args.educationBackgrounds = educationBackgrounds - } - if (!isEmpty(workBackgrounds)) args.workBackgrounds = workBackgrounds - if (!isEmpty(social)) args.social = social - - store.mark({ updating: true }) - log('args: ', args) - sr71$.mutate(S.updateProfile, args) -} - -export const cancelEdit = () => send(EVENT.DRAWER.CLOSE) - -export const updateDone = () => { - const editing = cast(updateFields, store.editUserData) - store.updateAccount(editing) -} - -export const toggleSocials = () => - store.mark({ showSocials: !store.showSocials }) - -const cancelLoading = () => store.mark({ updating: false }) - -// ############################### -// Data & Error handlers -// ############################### - -const DataSolver = [ - { - match: asyncRes('updateProfile'), - action: () => { - meteorState(store, 'success', 3) - updateDone() - cancelLoading() - }, - }, -] - -const ErrSolver = [ - { - match: asyncErr(ERR.GRAPHQL), - action: () => cancelLoading(), - }, - { - match: asyncErr(ERR.TIMEOUT), - action: ({ details }) => { - cancelLoading() - errRescue({ type: ERR.TIMEOUT, details, path: 'AccountEditor' }) - }, - }, - { - match: asyncErr(ERR.NETWORK), - action: () => { - cancelLoading() - errRescue({ type: ERR.NETWORK, path: 'AccountEditor' }) - }, - }, -] - -// ############################### -// init & uninit -// ############################### -export const useInit = (_store) => - useEffect(() => { - store = _store - // log('effect init') - sub$ = sr71$.data().subscribe($solver(DataSolver, ErrSolver)) - store.copyAccountInfo() - - return () => { - // log('effect uninit') - sr71$.stop() - sub$.unsubscribe() - } - }, [_store]) diff --git a/src/containers/editor/AccountEditor/logic.tsx b/src/containers/editor/AccountEditor/logic.tsx new file mode 100755 index 000000000..df26c47da --- /dev/null +++ b/src/containers/editor/AccountEditor/logic.tsx @@ -0,0 +1,96 @@ +import { useEffect } from 'react' + +import type { TEditValue } from '@/spec' +import { ERR } from '@/constant' +import { closeDrawer } from '@/utils/helper' + +import { buildLog, asyncSuit, updateEditing, errRescue } from '@/utils' + +import type { TStore } from './store' +import S from './schema' + +/* eslint-disable-next-line */ +const log = buildLog('L:AccountEditor') + +const { SR71, $solver, asyncRes, asyncErr } = asyncSuit +const sr71$ = new SR71() + +let store: TStore | undefined +let sub$ = null + +export const inputOnChange = (e: TEditValue, key: string): void => { + updateEditing(store, key, e) +} + +export const onUpdate = (): void => { + if (!store.isReady) return + + console.log('onUpdate: ', store.editData) + store.mark({ publishing: true }) + sr71$.mutate(S.updateProfile, store.editData) +} + +const cancelLoading = () => store.mark({ publishing: false }) + +const loadUser = () => { + const { viewingUser } = store + sr71$.query(S.user, { login: viewingUser.login }) +} + +// ############################### +// Data & Error handlers +// ############################### + +const DataSolver = [ + { + match: asyncRes('updateProfile'), + action: () => { + store.updateAccount() + store.mark({ publishing: false, publishDone: true }) + closeDrawer() + }, + }, + { + match: asyncRes('user'), + action: ({ user }) => store.loadUser(user), + }, +] + +const ErrSolver = [ + { + match: asyncErr(ERR.GRAPHQL), + action: () => cancelLoading(), + }, + { + match: asyncErr(ERR.TIMEOUT), + action: ({ details }) => { + cancelLoading() + errRescue({ type: ERR.TIMEOUT, details, path: 'AccountEditor' }) + }, + }, + { + match: asyncErr(ERR.NETWORK), + action: () => { + cancelLoading() + errRescue({ type: ERR.NETWORK, path: 'AccountEditor' }) + }, + }, +] + +// ############################### +// init & uninit +// ############################### +export const useInit = (_store: TStore): void => + useEffect(() => { + store = _store + // log('effect init') + sub$ = sr71$.data().subscribe($solver(DataSolver, ErrSolver)) + loadUser() + + return () => { + // log('effect uninit') + store.reset() + sr71$.stop() + sub$.unsubscribe() + } + }, [_store]) diff --git a/src/containers/editor/AccountEditor/schema.ts b/src/containers/editor/AccountEditor/schema.ts index d497cae48..2b4841575 100755 --- a/src/containers/editor/AccountEditor/schema.ts +++ b/src/containers/editor/AccountEditor/schema.ts @@ -1,36 +1,38 @@ import { gql } from '@urql/core' +const user = gql` + query ($login: String!) { + user(login: $login) { + login + avatar + nickname + bio + shortbio + email + location + sex + fromGithub + social { + github + blog + company + twitter + } + } + } +` + const updateProfile = gql` - mutation( - $profile: UserProfileInput! - $social: SocialInput - $educationBackgrounds: [EduBackgroundInput] - $workBackgrounds: [WorkBackgroundInput] - ) { - updateProfile( - profile: $profile - social: $social - educationBackgrounds: $educationBackgrounds - workBackgrounds: $workBackgrounds - ) { + mutation ($profile: UserProfileInput!, $social: SocialInput) { + updateProfile(profile: $profile, social: $social) { id } } ` -export const updateFields = [ - 'nickname', - 'email', - 'location', - 'bio', - 'sex', - // social - 'social', - // backgrounds - 'workBackgrounds', - 'educationBackgrounds', -] - -export const S = { +const schema = { updateProfile, + user, } + +export default schema diff --git a/src/containers/editor/AccountEditor/spec.ts b/src/containers/editor/AccountEditor/spec.ts new file mode 100644 index 000000000..53a18fa59 --- /dev/null +++ b/src/containers/editor/AccountEditor/spec.ts @@ -0,0 +1,17 @@ +export type TEditData = { + profile: { + avatar: string + nickname: string + bio: string + shortbio: string + sex: string + location: string + email: string + } + social: { + github: string + twitter: string + company: string + blog: string + } +} diff --git a/src/containers/editor/AccountEditor/store.js b/src/containers/editor/AccountEditor/store.js deleted file mode 100755 index c5663c36a..000000000 --- a/src/containers/editor/AccountEditor/store.js +++ /dev/null @@ -1,141 +0,0 @@ -/* - * AccountEditorStore store - * - */ - -import { types as T, getParent } from 'mobx-state-tree' -import { merge, clone, concat } from 'ramda' - -import { markStates, toJS, flashState } from '@/utils/mobx' -import { changeset } from '@/utils/validator' -import { User, EduBackground, WorkBackground } from '@/model' - -const AccountEditorStore = T.model('AccountEditorStore', { - // user: T.optional(User, {}), - editUser: T.optional(User, {}), - showSocials: T.optional(T.boolean, false), - - educationBg: T.optional(EduBackground, { school: '', major: '' }), - workBg: T.optional(WorkBackground, { company: '', title: '' }), - - updating: T.optional(T.boolean, false), - success: T.optional(T.boolean, false), - error: T.optional(T.boolean, false), - warn: T.optional(T.boolean, false), - statusMsg: T.optional(T.string, ''), - ratKey: T.optional(T.string, ''), -}) - .views((self) => ({ - get root() { - return getParent(self) - }, - get statusClean() { - const { success, error, warn } = self - return !success && !error && !warn - }, - get editUserData() { - return { - ...toJS(self.editUser), - } - }, - get educationBgData() { - return toJS(self.educationBg) - }, - get workBgData() { - return toJS(self.workBg) - }, - get accountOrigin() { - return self.root.account.accountInfo - }, - })) - .actions((self) => ({ - changesetErr(options) { - self.root.changesetErr(options) - }, - validator(type) { - const { workBackgrounds, educationBackgrounds } = self.editUserData - - switch (type) { - case 'work': { - const result = changeset(self.workBgData) - .exist({ company: '公司名称' }, self.changesetErr) - .min({ company: '公司名称' }, 2, self.changesetErr) - .alreadyExists( - { - company: `${self.workBgData.company}, ${self.workBgData.title}`, - }, - self.workBgData, - workBackgrounds, - self.changesetErr, - ) - .done() - - if (!result.passed) flashState(self, 'ratKey', result.rat) - return result.passed - } - case 'education': { - const { educationBgData } = self - const result = changeset(educationBgData) - .exist({ school: '学校名称' }, self.changesetErr) - .min({ school: '学校名称' }, 2, self.changesetErr) - .alreadyExists( - { - school: `${educationBgData.school}, ${educationBgData.major}`, - }, - educationBgData, - educationBackgrounds, - self.changesetErr, - ) - .done() - - if (!result.passed) flashState(self, 'ratKey', result.rat) - return result.passed - } - default: { - return false - } - } - }, - - copyAccountInfo() { - const { accountInfo } = self.root.account - if (accountInfo !== {}) { - self.editUser = accountInfo - } - }, - - updateAccount(user) { - self.root.account.updateAccount(user) - self.root.updateViewingIfNeed('user', user) - }, - - updateEditing(sobj) { - const editUser = merge(self.editUser, { ...sobj }) - self.mark({ editUser }) - }, - - addBackground(type) { - if (!self.validator(type)) return false - - if (type === 'work') { - let workBackgrounds = clone(self.editUserData.workBackgrounds) - workBackgrounds = concat([self.workBgData], workBackgrounds) - - self.updateEditing({ workBackgrounds }) - return self.mark({ workBg: { company: '', title: '' } }) - } - - let educationBackgrounds = clone(self.editUserData.educationBackgrounds) - educationBackgrounds = concat( - [self.educationBgData], - educationBackgrounds, - ) - self.updateEditing({ educationBackgrounds }) - self.mark({ educationBg: { school: '', major: '' } }) - }, - mark(sobj) { - markStates(sobj, self) - }, - })) - -export default AccountEditorStore diff --git a/src/containers/editor/AccountEditor/store.tsx b/src/containers/editor/AccountEditor/store.tsx new file mode 100755 index 000000000..3f35c5818 --- /dev/null +++ b/src/containers/editor/AccountEditor/store.tsx @@ -0,0 +1,117 @@ +/* + * AccountEditorStore store + * + */ + +import { types as T, getParent, Instance } from 'mobx-state-tree' +import { reduce, keys, merge, pick, startsWith } from 'ramda' + +import type { TRootStore, TSubmitState, TUser } from '@/spec' +import { markStates, toJS } from '@/utils/mobx' + +import type { TEditData } from './spec' +import { SEX } from './constant' + +const safeMap = (obj) => { + return reduce( + merge, + {}, + keys(obj).map((key) => { + if (key === 'fromGithub') { + return { + fromGithub: obj[key] || false, + } + } + return { + [key]: obj[key] || '', + } + }), + ) +} + +const AccountEditorStore = T.model('AccountEditorStore', { + avatar: T.optional(T.string, ''), + login: T.optional(T.string, ''), + fromGithub: T.optional(T.boolean, false), + nickname: T.optional(T.string, ''), + shortbio: T.optional(T.string, ''), + bio: T.optional(T.string, ''), + sex: T.optional(T.string, SEX.DUDE), + location: T.optional(T.string, ''), + company: T.optional(T.string, ''), + + // social + github: T.optional(T.string, ''), + twitter: T.optional(T.string, ''), + email: T.optional(T.string, ''), + blog: T.optional(T.string, ''), + + publishing: T.optional(T.boolean, false), + publishDone: T.optional(T.boolean, false), +}) + .views((self) => ({ + get editData(): TEditData { + const profile = pick( + ['avatar', 'nickname', 'shortbio', 'bio', 'sex', 'location', 'email'], + self, + ) + const social = pick(['github', 'twitter', 'company', 'blog'], self) + + return { + profile, + social, + } + }, + get viewingUser(): TUser { + const root = getParent(self) as TRootStore + return toJS(root.viewing.user) + }, + get isReady(): boolean { + return true + }, + get submitState(): TSubmitState { + const slf = self as TStore + return pick(['publishing', 'publishDone', 'isReady'], slf) + }, + })) + .actions((self) => ({ + loadUser(user: TUser): void { + const { social, ...rest } = user + const slf = self as TStore + + slf.mark({ ...safeMap(social), ...safeMap(rest) }) + + if (social.github && startsWith('https://github.com/', social.github)) { + self.github = social.github.slice(19) + } + }, + updateAccount(): void { + const slf = self as TStore + const root = getParent(self) as TRootStore + + const user = { + ...slf.editData.profile, + social: slf.editData.social, + } + + root.account.updateAccount(user) + root.updateViewingIfNeed('user', user) + }, + + updateEditing(sobj): void { + const slf = self as TStore + slf.mark(sobj) + }, + + reset(): void { + self.publishing = false + self.publishDone = false + }, + + mark(sobj: Record): void { + markStates(sobj, self) + }, + })) + +export type TStore = Instance +export default AccountEditorStore diff --git a/src/containers/editor/AccountEditor/styles/background_inputer.ts b/src/containers/editor/AccountEditor/styles/background_inputer.ts deleted file mode 100755 index 25978d2ff..000000000 --- a/src/containers/editor/AccountEditor/styles/background_inputer.ts +++ /dev/null @@ -1,76 +0,0 @@ -import styled from 'styled-components' - -import Img from '@/Img' -import { theme } from '@/utils/themes' -import css from '@/utils/css' - -export const Wrapper = styled.div` - ${css.flexColumn()}; -` -export const FormItemWrapper = styled.div` - ${css.flex()}; - margin-bottom: 20px; -` -export const FormLabel = styled.div` - text-align: right; - font-size: 0.9rem; - color: ${theme('form.label')}; - margin-right: 10px; - margin-top: 5px; - width: 75px; - margin-left: -12px; -` -export const FormInput = styled.div` - ${css.flex('align-center')}; - width: 280px; -` -export const Adder = styled(Img)` - fill: ${theme('banner.desc')}; - ${css.size(25)}; - margin-left: 5px; - margin-top: 5px; - &:hover { - fill: ${theme('banner.title')}; - cursor: pointer; - } - transition: fill 0.3s; -` -export const BackgroundsWrapper = styled.div` - ${css.flexColumn()}; - margin-top: -10px; - margin-bottom: 20px; - margin-left: 22%; -` -export const BackgroundItem = styled.div` - ${css.flex('align-center')}; -` -export const BgTitle = styled.div` - color: ${theme('banner.desc')}; - font-weight: bold; -` - -export const BgDivider = styled.div` - color: ${theme('banner.desc')}; - font-size: 1.2rem; - margin-left: 3px; - margin-right: 3px; - margin-top: -3px; -` - -export const BgDesc = styled.div` - color: ${theme('banner.desc')}; -` - -export const DeleteIcon = styled(Img)` - fill: ${theme('banner.desc')}; - ${css.size(12)}; - margin-left: 8px; - opacity: 0; - ${BackgroundItem}:hover & { - fill: ${theme('banner.title')}; - visibility: visible; - opacity: 1; - cursor: pointer; - } - transition: opacity 0.3s; -` diff --git a/src/containers/editor/AccountEditor/styles/index.ts b/src/containers/editor/AccountEditor/styles/index.ts index 6d1ae958f..d8c381e99 100755 --- a/src/containers/editor/AccountEditor/styles/index.ts +++ b/src/containers/editor/AccountEditor/styles/index.ts @@ -3,7 +3,9 @@ import styled from 'styled-components' import Img from '@/Img' import { theme } from '@/utils/themes' import css from '@/utils/css' -import animate from '@/utils/animations' + +import GithubSVG from '@/icons/Github8' +import FormInput from '@/widgets/Input' export const Wrapper = styled.div` ${css.flexColumn('align-center')}; @@ -15,37 +17,82 @@ export const Wrapper = styled.div` margin-top: 15px; margin-left: 15px; margin-right: 15px; - background: ${theme('content.cardBg')}; border-radius: 5px; position: relative; - animation: ${animate.fadeInRight} 0.2s linear; ` export const AvatarPic = styled(Img)` - ${css.circle(70)}; - margin-bottom: 30px; -` -export const BackIcon = styled(Img)` - fill: ${theme('font')}; - ${css.size(20)}; - position: absolute; - top: 13px; - left: 18px; - cursor: pointer; - opacity: 0.6; - - &:hover { - opacity: 1; - } - transition: opacity 0.2s; + ${css.size(140)}; + border-radius: 42%; + border: 4px solid; + border-color: #043443; + margin-bottom: 50px; + margin-left: -5px; ` export const FormsWrapper = styled.div` ${css.flexColumn('align-center')}; ` -export const Divider = styled.div` - border-top: 1px solid; - border-top-color: ${theme('drawer.divider')}; - margin-top: 15px; - width: 75%; - margin-bottom: 20px; +export const Section = styled.div` + width: 100%; + margin-bottom: 25px; + outline: none; +` +export const RowSection = styled.div` + ${css.flex('align-center')}; + margin-bottom: 28px; +` +export const LoginSection = styled.div` + width: 100%; + ${css.flex('align-center')}; + margin-bottom: 15px; +` +export const LoginDesc = styled.div` + color: ${theme('thread.articleTitle')}; + margin-bottom: 8px; +` +export const GithubIcon = styled(GithubSVG)` + ${css.size(15)}; + fill: ${theme('thread.articleDigest')}; + opacity: 0.5; + margin-right: 20px; + margin-bottom: 7px; +` +export const SectionHint = styled.div` + color: ${theme('thread.articleDigest')}; + opacity: 0.8; + font-size: 12px; + margin-top: 6px; + margin-left: 12px; +` +export const Label = styled.div` + ${css.flex('justify-between', 'align-end')}; + color: ${theme('thread.articleDigest')}; + font-size: 14px; + margin-bottom: 8px; + margin-left: 5px; + margin-right: 10px; + opacity: 0.85; +` +export const SexLabel = styled.div` + color: ${theme('thread.articleDigest')}; + font-size: 14px; + margin-left: 7px; + margin-right: 10px; + opacity: 0.85; +` +export const Input = styled(FormInput)` + text-align: left; + padding: 5px 5px; + padding-left: 12px; + height: 36px; + width: 280px; + font-size: 15px; +` +export const TextareaInput = styled(Input)` + padding: 10px; + padding-left: 12px; + font-size: 14px; + line-height: 1.5; +` +export const Footer = styled.div` + margin-left: -15px; ` -export const ActionBtns = styled.div`` diff --git a/src/containers/editor/AccountEditor/styles/sex_inputer.ts b/src/containers/editor/AccountEditor/styles/sex_inputer.ts index 3ce568a81..3da29f6e7 100755 --- a/src/containers/editor/AccountEditor/styles/sex_inputer.ts +++ b/src/containers/editor/AccountEditor/styles/sex_inputer.ts @@ -1,40 +1,35 @@ import styled from 'styled-components' -import Img from '@/Img' import { theme } from '@/utils/themes' import css from '@/utils/css' +import ManSVG from '@/icons/Man' +import WomanSVG from '@/icons/Woman' + +import { SEX } from '../constant' + export const Wrapper = styled.div` ${css.flex()}; - margin-bottom: 25px; + width: 250px; ` -const SexIcon = styled(Img)` +const sexIcon = ` ${css.size(20)}; - margin-right: 10px; + margin-right: 6px; margin-left: 5px; cursor: pointer; ` - -/* fill: ${props => */ -/* props.active === props.item ? theme('font', props) : 'grey'}; */ -export const Dude = styled.div`` -export const Girl = styled.div`` -export const DudeIcon = styled(SexIcon)<{ value: string }>` +export const DudeIcon = styled(ManSVG)<{ value: string }>` + ${sexIcon}; fill: ${({ value }) => - value === 'dude' ? '#869eec' : theme('drawer.divider')}; + value === SEX.DUDE + ? theme('baseColor.blue') + : theme('thread.articleDigest')}; ` - -export const GirlIcon = styled(SexIcon)<{ value: string }>` - fill: ${({ value }) => (value === 'girl' ? 'pink' : theme('drawer.divider'))}; +export const GirlIcon = styled(WomanSVG)<{ value: string }>` + ${sexIcon}; + fill: ${({ value }) => + value === SEX.GIRL + ? theme('baseColor.pink') + : theme('thread.articleDigest')}; margin-top: 1px; ` -export const SexLable = styled.div` - font-size: 1em; - color: ${theme('form.label')}; - margin-right: 10px; -` - -export const SexInput = styled.div` - ${css.flex()}; - width: 250px; -` diff --git a/src/containers/editor/AccountEditor/styles/social_inputer.ts b/src/containers/editor/AccountEditor/styles/social_inputer.ts index b295d6bfb..9aa6f7df9 100755 --- a/src/containers/editor/AccountEditor/styles/social_inputer.ts +++ b/src/containers/editor/AccountEditor/styles/social_inputer.ts @@ -1,95 +1,77 @@ import styled from 'styled-components' -import type { TActive } from '@/spec' -import Img from '@/Img' import { theme } from '@/utils/themes' import css from '@/utils/css' -export const Wrapper = styled.div`` -export const InputWrapper = styled.div` - display: ${({ show }) => (show ? 'block' : 'none')}; -` +import FormInput from '@/widgets/Input' -export const FormItemWrapper = styled.div` - ${css.flex()}; - margin-bottom: 20px; -` -export const FormLabel = styled.div` - text-align: right; - font-size: 0.9rem; - color: ${theme('form.label')}; - margin-right: 10px; - margin-top: 5px; - width: 75px; - margin-left: -35px; -` +import MailSVG from '@/icons/Mail' +import TwitterSVG from '@/icons/Twitter' +import BlogSVG from '@/icons/Blog' +import GithubSVG from '@/icons/Github8' +import CitySVG from '@/icons/City' +import CompanySVG from '@/icons/Company' -export const FormInput = styled.div` - width: 250px; +export const Input = styled(FormInput)` + text-align: left; + padding: 5px 5px; + padding-left: 12px; + height: 36px; + width: 245px; + font-size: 15px; ` -export const SocialIconsWrapper = styled.div` - ${css.flex()}; - flex-wrap: wrap; - width: 250px; +export const Section = styled.div` + ${css.flex('align-center')}; + margin-bottom: 20px; + width: 100%; + margin-left: 12px; ` -export const SocialIcon = styled(Img)` - fill: ${({ active }) => - active ? theme('banner.title') : theme('banner.desc')}; +const iconBase = ` ${css.size(18)}; - margin-right: 8px; - transition: fill 0.3s; - opacity: ${({ active }) => (active ? 1 : 0.8)}; -` -export const TogglerWrapper = styled.div` - display: ${({ show }) => (show ? 'flex' : 'none')}; -` -export const TogglerLabelWrapper = styled(TogglerWrapper)` - margin-left: 5%; + margin-right: 10px; ` -export const TogglerDivider = styled.div` - margin-bottom: 25px; - border-bottom: 1px dashed; - border-color: ${theme('banner.desc')}; - opacity: 0.7; - width: 32%; - margin-top: 8px; +export const CityIcon = styled(CitySVG)` + ${iconBase}; + ${css.size(16)}; + fill: ${theme('thread.articleDigest')}; + margin-left: 1px; + margin-right: 11px; ` -export const TogglerTextWrapper = styled.div` - ${css.flex()}; - &:hover { - cursor: pointer; - font-weight: bold; - } +export const CompanyIcon = styled(CompanySVG)` + ${iconBase}; + fill: ${theme('thread.articleDigest')}; ` -export const TogglerLabelText = styled.div` - color: ${theme('banner.desc')}; - font-size: 0.7rem; - border: 1px solid; - border-color: ${theme('banner.desc')}; - border-radius: 2px; - align-self: start; - margin-left: 10px; - margin-right: 10px; - padding: 0 5px; - &:hover { - cursor: pointer; - border-color: ${theme('banner.title')}; - color: ${theme('banner.title')}; - } - - transition: color 0.3s; +export const MailIcon = styled(MailSVG)` + ${iconBase}; + ${css.size(13)}; + margin-left: 1px; + margin-right: 11px; + margin-top: 1px; + fill: ${theme('thread.articleDigest')}; ` - -export const TogglerText = styled.div` - color: ${theme('banner.title')}; -` -const ToggleIcon = styled(Img)` - fill: ${theme('banner.title')}; +export const GithubIcon = styled(GithubSVG)` + ${iconBase}; ${css.size(15)}; - margin-left: 3px; - margin-top: 2px; + margin-right: 11px; + fill: ${theme('thread.articleDigest')}; +` +export const TwitterIcon = styled(TwitterSVG)` + ${iconBase}; + ${css.size(14)}; + margin-right: 11px; + fill: ${theme('thread.articleDigest')}; ` -export const UpIcon = styled(ToggleIcon)` - transform: rotateX(180deg); +export const BlogIcon = styled(BlogSVG)` + ${iconBase}; + fill: ${theme('thread.articleDigest')}; + margin-right: 9px; + margin-top: -1px; ` -export const DownIcon = styled(ToggleIcon)`` +export const ICON = { + CityIcon, + CompanyIcon, + MailIcon, + GithubIcon, + TwitterIcon, + BlogIcon, +} diff --git a/src/containers/editor/ArticleEditor/PublishRules/PostRules.tsx b/src/containers/editor/ArticleEditor/PublishRules/PostRules.tsx index 16885fa9b..b21b58059 100644 --- a/src/containers/editor/ArticleEditor/PublishRules/PostRules.tsx +++ b/src/containers/editor/ArticleEditor/PublishRules/PostRules.tsx @@ -10,19 +10,21 @@ const PublishRules: FC = () => {
  • 友善是一切有意义讨论的基础和前提。Don't be an asshole.
  • 如果是寻求解答,请务必提供上下文、需求场景等必要信息, 并勾选发布上方的 - “求助 / 提问” 选框。这里崇尚具体实际的问题,请慎用 “如何看待 xxx” + “求助 / 提问” 选框。这里崇尚具体、实际的问题,请慎用 “如何看待 xxx” 开头。
  • - 如果不确定内容该发布到哪个社区,可以先发布到 /draft,并 @ - 相关社区的志愿者,他们很乐于给你反馈。 + 卖课,领资料,公众号引流等骚操作一月禁言起步。多次发布,停止服务。 +
  • +
  • + 请尊重自己和他人的时间,不要发布无意义的黑话 / 烂梗 / Trash Talk + 以及语焉不详的一句话问题等。
  • -
  • 卖课,领资料,公众号引流等一月禁言起步。多次发布,停止服务。
  • +
  • 遵法守纪,尊重版权,尊重事实。勿议国是,不搞沸腾,不乱贴标签。
  • - 请尊重自己和他人的时间,不要发布无意义的烂梗 / 黑话, 以及排版糟糕的 - Trash Talk. + 如果不确定内容该发布到哪个社区,可以先发布到 /draft,并 @ + 相关社区的志愿者,他们很乐于给你反馈。
  • -
  • 严禁侵权,勿议国是,尊重事实,不搞沸腾。
  • ) diff --git a/src/containers/editor/CommunityEditor/schema.ts b/src/containers/editor/CommunityEditor/schema.ts index f24e801ad..6f30588f9 100755 --- a/src/containers/editor/CommunityEditor/schema.ts +++ b/src/containers/editor/CommunityEditor/schema.ts @@ -10,7 +10,7 @@ const pagedCommunities = gql` subscribersCount viewerHasSubscribed @include(if: $userHasLogin) } - ${F.pagedCounts} + ${F.pagi} } } ` @@ -24,7 +24,7 @@ const searchCommunities = gql` subscribersCount viewerHasSubscribed @include(if: $userHasLogin) } - ${F.pagedCounts} + ${F.pagi} } } ` @@ -42,14 +42,14 @@ const subscribeCommunity = gql` } ` const unsubscribeCommunity = gql` - mutation($communityId: ID!) { + mutation ($communityId: ID!) { unsubscribeCommunity(communityId: $communityId) { id } } ` const pagedCategories = gql` - query($filter: PagedFilter!) { + query ($filter: PagedFilter!) { pagedCategories(filter: $filter) { entries { id diff --git a/src/containers/editor/WorksEditor/Content/BasicInfoPart/index.tsx b/src/containers/editor/WorksEditor/Content/BasicInfoPart/index.tsx index f3f3b9169..a5d712310 100755 --- a/src/containers/editor/WorksEditor/Content/BasicInfoPart/index.tsx +++ b/src/containers/editor/WorksEditor/Content/BasicInfoPart/index.tsx @@ -160,7 +160,7 @@ const BasicInfoPart: FC = ({ inputData, socialOptions }) => { } }} > - 业余项目(Side Project) + 副业项目(Side Project) = ({ articlesThread: store }) => { filtersData, curThread, showFilters, - viewingArticle, c11n, + resState, } = store - const { bannerLayout } = c11n - - const resState = store.resState as TResState const { pageNumber, totalCount } = pagedArticlesData - console.log('## pagedArticlesData: ', pagedArticlesData) - return ( @@ -59,7 +54,7 @@ const ArticlesThreadContainer: FC = ({ articlesThread: store }) => { {showFilters && ( = ({ articlesThread: store }) => { diff --git a/src/containers/thread/ArticlesThread/logic.ts b/src/containers/thread/ArticlesThread/logic.ts index c66e57bb3..b65f440f5 100755 --- a/src/containers/thread/ArticlesThread/logic.ts +++ b/src/containers/thread/ArticlesThread/logic.ts @@ -12,7 +12,6 @@ import { matchPagedArticles, matchArticleUpvotes } from '@/utils/macros' import type { TStore } from './store' import S from './schema' -import { threadOnChange } from '@/containers/user/UserPublishedComments/logic' /* eslint-disable-next-line */ const log = buildLog('L:ArticlesThread') diff --git a/src/containers/tool/ArticleSticker/schema.ts b/src/containers/tool/ArticleSticker/schema.ts index ecd8e3897..84e67fa26 100644 --- a/src/containers/tool/ArticleSticker/schema.ts +++ b/src/containers/tool/ArticleSticker/schema.ts @@ -7,7 +7,7 @@ import { F } from '@/schemas' // entries { // ${F.author} // } -// ${F.pagedCounts} +// ${F.pagi} // } // } diff --git a/src/containers/tool/Drawer/AddOn/index.tsx b/src/containers/tool/Drawer/AddOn/index.tsx index 662d9ee1e..3a9f07e63 100755 --- a/src/containers/tool/Drawer/AddOn/index.tsx +++ b/src/containers/tool/Drawer/AddOn/index.tsx @@ -20,12 +20,13 @@ type TProps = { const AddOn: FC = ({ type, articleNavi }) => { const showArticleNavi = includes(type, ARTICLE_VIEWER_TYPES) + const showShare = includes(type, ARTICLE_VIEWER_TYPES) return ( - + - + {showShare && } {showArticleNavi && } diff --git a/src/containers/tool/Drawer/dynamics.tsx b/src/containers/tool/Drawer/dynamics.tsx index 8c705f0d5..92f55f08f 100755 --- a/src/containers/tool/Drawer/dynamics.tsx +++ b/src/containers/tool/Drawer/dynamics.tsx @@ -4,7 +4,15 @@ import dynamic from 'next/dynamic' import { LavaLampLoading } from '@/widgets/dynamic' import EditorLoading from '@/widgets/Loading/EditorLoading' -const CommonLoading = () => +import { LavaLoadingWrapper } from './styles' + +const CommonLoading = () => { + return ( + + + + ) +} // common style loading config const commonConfig = { @@ -38,7 +46,7 @@ export const RepoViewer = dynamic( // editors export const AccountEditor = dynamic( () => import('@/containers/editor/AccountEditor'), - editorConfig, + commonConfig, ) // export const RepoEditor = dynamic( diff --git a/src/containers/tool/Drawer/store.ts b/src/containers/tool/Drawer/store.ts index 67d489ce0..184dfb1ba 100755 --- a/src/containers/tool/Drawer/store.ts +++ b/src/containers/tool/Drawer/store.ts @@ -141,7 +141,7 @@ const DrawerStore = T.model('DrawerStore', { .actions((self) => ({ open({ type, data, options = {} }): void { const slf = self as TStore - const thread = data.meta?.thread?.toLowerCase() + const thread = data?.meta?.thread?.toLowerCase() if (type === TYPE.DRAWER.MODELINE_MENU) { slf.mmType = data @@ -162,7 +162,9 @@ const DrawerStore = T.model('DrawerStore', { slf.canBeClose = false } - slf.markPreviewURLIfNeed(data) + if (type !== TYPE.DRAWER.ACCOUNT_EDIT) { + slf.markPreviewURLIfNeed(data) + } }, setViewing(sobj: Record): void { const root = getParent(self) as TRootStore diff --git a/src/containers/tool/Drawer/styles/add_on/index.ts b/src/containers/tool/Drawer/styles/add_on/index.ts index a07cd0f8e..5878ab236 100755 --- a/src/containers/tool/Drawer/styles/add_on/index.ts +++ b/src/containers/tool/Drawer/styles/add_on/index.ts @@ -1,6 +1,5 @@ import styled from 'styled-components' -import { TYPE } from '@/constant' import { theme } from '@/utils/themes' import css from '@/utils/css' @@ -12,14 +11,14 @@ export const Wrapper = styled.div` width: 0; `}; ` -export const TopArea = styled.div<{ type: string }>` +export const TopArea = styled.div<{ showShare: boolean }>` width: 26px; - height: 80px; + height: ${({ showShare }) => (showShare ? '80px' : '50px')}; + position: absolute; top: 0; left: 34px; - display: ${({ type }) => - type === TYPE.DRAWER.ACCOUNT_EDIT ? 'none' : 'block'}; + display: block; background: #002a34; border-bottom-left-radius: 15px; box-shadow: ${theme('drawer.shadow')}; diff --git a/src/containers/tool/Drawer/styles/index.ts b/src/containers/tool/Drawer/styles/index.ts index 0e2f1a6b4..c6dd3df28 100755 --- a/src/containers/tool/Drawer/styles/index.ts +++ b/src/containers/tool/Drawer/styles/index.ts @@ -125,3 +125,8 @@ export const PreviewHeader = styled.div` border-bottom: 1px solid grey; line-height: 30px; ` + +export const LavaLoadingWrapper = styled.div` + margin-left: 45%; + margin-top: 160px; +` diff --git a/src/containers/tool/Share/constant.ts b/src/containers/tool/Share/constant.ts index 7b5193413..757a23072 100644 --- a/src/containers/tool/Share/constant.ts +++ b/src/containers/tool/Share/constant.ts @@ -9,7 +9,6 @@ export const OUTSIDE_SHARE_TYPE = { EMAIL: 'email', WECHAT: 'wechat', TELEGRAM: 'telegram', - // QQ: 'qq', WEIBO: 'weibo', DOUBAN: 'douban', FACEBOOK: 'facebook', diff --git a/src/containers/unit/Comments/Comment/Actions.tsx b/src/containers/unit/Comments/Comment/Actions.tsx index 134081677..e1055bb75 100755 --- a/src/containers/unit/Comments/Comment/Actions.tsx +++ b/src/containers/unit/Comments/Comment/Actions.tsx @@ -11,10 +11,6 @@ import { SpaceGrow } from '@/widgets/Common' import { Wrapper, ReplyAction, MoreWrapper } from '../styles/comment/actions' import { openUpdateEditor, openReplyEditor } from '../logic' -type TProps = { - data: TComment -} - const menuOptions = [ // { // key: 'quote', @@ -33,6 +29,10 @@ const menuOptions = [ }, ] +type TProps = { + data: TComment +} + const Actions: FC = ({ data }) => { const { user } = useAccount() diff --git a/src/containers/unit/Comments/Comment/DesktopView/DefaultLayout.tsx b/src/containers/unit/Comments/Comment/DesktopView/DefaultLayout.tsx index df9096201..bab8db27c 100644 --- a/src/containers/unit/Comments/Comment/DesktopView/DefaultLayout.tsx +++ b/src/containers/unit/Comments/Comment/DesktopView/DefaultLayout.tsx @@ -13,6 +13,7 @@ import Header from '../Header' import ReplyBar from '../ReplyBar' import Footer from '../Footer' +import type { TAPIMode } from '../../spec' import { Wrapper, CommentWrapper, @@ -38,6 +39,7 @@ const getSelection = () => { type TProps = { data: TComment + apiMode: TAPIMode isReply?: boolean showInnerRef?: boolean } @@ -46,6 +48,7 @@ const DefaultLayout: FC = ({ data, isReply = false, showInnerRef = false, + apiMode, }) => { const { isPinned, meta } = data const { isArticleAuthorUpvoted } = meta @@ -92,7 +95,7 @@ const DefaultLayout: FC = ({ -
    +
    {!isReply && data.replyTo && } = ({ mode="comment" /> -