From a180b89e8fc851d6540d682e02264dc81616c861 Mon Sep 17 00:00:00 2001 From: mydearxym Date: Mon, 11 Oct 2021 14:17:57 +0800 Subject: [PATCH 01/30] refactor(workflow): query target community & fix mobx-react issue --- src/components/Cards/styles/community_card.ts | 6 ++- src/components/ErrorPage/index.tsx | 1 - .../editor/ArticleEditor/CommunityBadge.tsx | 20 ++++++++-- src/containers/editor/ArticleEditor/index.tsx | 3 +- src/containers/editor/ArticleEditor/logic.ts | 37 ++++++++++++++++++- src/containers/editor/ArticleEditor/schema.ts | 21 +++++------ src/containers/editor/ArticleEditor/store.ts | 12 ++++-- .../ArticleEditor/styles/community_badge.ts | 5 ++- src/containers/editor/RichEditor/index.tsx | 3 ++ src/pages/editor.tsx | 12 ++++++ 10 files changed, 94 insertions(+), 26 deletions(-) diff --git a/src/components/Cards/styles/community_card.ts b/src/components/Cards/styles/community_card.ts index c7b62e012..a87ae80de 100644 --- a/src/components/Cards/styles/community_card.ts +++ b/src/components/Cards/styles/community_card.ts @@ -2,16 +2,18 @@ import styled from 'styled-components' // import type { TTestable } from '@/spec' -import Img from '@/Img' +// import Img from '@/Img' import { theme } from '@/utils/themes' import css from '@/utils/css' +import CommunityFaceLogo from '@/components/CommunityFaceLogo' + export const Wrapper = styled.div` ${css.flexColumn()}; width: 200px; min-height: 100px; ` -export const CommunityLogo = styled(Img)` +export const CommunityLogo = styled(CommunityFaceLogo)` ${css.size(30)}; ` export const SubsCount = styled.div` diff --git a/src/components/ErrorPage/index.tsx b/src/components/ErrorPage/index.tsx index fec3f5f26..e085fc953 100755 --- a/src/components/ErrorPage/index.tsx +++ b/src/components/ErrorPage/index.tsx @@ -10,7 +10,6 @@ import Link from 'next/link' import type { TMetric } from '@/spec' import { METRIC } from '@/constant' -import { ICON_BASE } from '@/config' import { buildLog } from '@/utils/logger' import SpinPlanet from './SpinPlanet' diff --git a/src/containers/editor/ArticleEditor/CommunityBadge.tsx b/src/containers/editor/ArticleEditor/CommunityBadge.tsx index 8143c1624..e8909490f 100644 --- a/src/containers/editor/ArticleEditor/CommunityBadge.tsx +++ b/src/containers/editor/ArticleEditor/CommunityBadge.tsx @@ -1,6 +1,11 @@ import { FC, memo } from 'react' +import type { TCommunity } from '@/spec' import { ICON_BASE } from '@/config' +import { cutRest } from '@/utils/helper' + +import Tooltip from '@/components/Tooltip' +import CommunityCard from '@/components/Cards/CommunityCard' import { Wrapper, @@ -13,15 +18,24 @@ import { ArrowLogo, } from './styles/community_badge' -const CommunityBadge: FC = () => { +type TProps = { + community: TCommunity +} + +const CommunityBadge: FC = ({ community }) => { return ( 发布到子社区: - <Logo src={`${ICON_BASE}/pl/javascript.png`} /> - <div>JavaScript</div> + <Logo src={community.logo} /> + <Tooltip + content={<CommunityCard item={community} />} + placement="bottom" + > + <div>{cutRest(community.title || '--', 15)}</div> + </Tooltip> <ChangeBtn> 更换 <ArrowLogo /> </ChangeBtn> diff --git a/src/containers/editor/ArticleEditor/index.tsx b/src/containers/editor/ArticleEditor/index.tsx index 207708abf..969a8b01e 100755 --- a/src/containers/editor/ArticleEditor/index.tsx +++ b/src/containers/editor/ArticleEditor/index.tsx @@ -39,6 +39,7 @@ const ArticleEditorContainer: FC<TProps> = ({ metric = METRIC.ARTICLE_EDITOR, }) => { useInit(store) + const { communityData } = store return ( <Wrapper testid={testid}> @@ -49,7 +50,7 @@ const ArticleEditorContainer: FC<TProps> = ({ <Footer /> </ContentWrapper> <div> - <CommunityBadge /> + <CommunityBadge community={communityData} /> <PublishRules /> </div> </InnerWrapper> diff --git a/src/containers/editor/ArticleEditor/logic.ts b/src/containers/editor/ArticleEditor/logic.ts index a58868ac2..d4d485137 100755 --- a/src/containers/editor/ArticleEditor/logic.ts +++ b/src/containers/editor/ArticleEditor/logic.ts @@ -2,11 +2,18 @@ import { useEffect } from 'react' // import { } from 'ramda' import { buildLog } from '@/utils/logger' +import asyncSuit from '@/utils/async' +import { getParameterByName } from '@/utils/route' +import type { TStore } from './store' import { STEP } from './constant' -// import S from './service' +import S from './schema' -let store = null +const { SR71, $solver, asyncRes } = asyncSuit +const sr71$ = new SR71() + +let sub$ = null +let store: TStore | undefined /* eslint-disable-next-line */ const log = buildLog('L:ArticleEditor') @@ -23,14 +30,40 @@ export const toggleSubTitle = (showSubTitle) => { store.mark({ showSubTitle }) } +export const loadCommunity = (communityRaw: string): void => { + sr71$.query(S.community, { raw: communityRaw.toLowerCase() }) +} + // ############################### // init & uninit handlers // ############################### +const DataSolver = [ + { + match: asyncRes('community'), + action: ({ community }) => { + console.log('community got: ', community) + store.mark({ community }) + }, + }, +] +const ErrSolver = [] + export const useInit = (_store) => { useEffect(() => { store = _store + sub$ = sr71$.data().subscribe($solver(DataSolver, ErrSolver)) log('useInit: ', store) + + const communityRaw = getParameterByName('community') + if (communityRaw) loadCommunity(communityRaw) + + // return () => store.reset() + return () => { + sr71$.stop() + sub$.unsubscribe() + sub$ = null + } // return () => store.reset() }, [_store]) } diff --git a/src/containers/editor/ArticleEditor/schema.ts b/src/containers/editor/ArticleEditor/schema.ts index 2ce6cebfc..36c32e92f 100755 --- a/src/containers/editor/ArticleEditor/schema.ts +++ b/src/containers/editor/ArticleEditor/schema.ts @@ -1,23 +1,20 @@ import { gql } from '@urql/core' -const simpleMutation = gql` - mutation($id: ID!) { - post(id: $id) { - id - } - } -` -const simpleQuery = gql` - query($filter: filter!) { - post(id: $id) { +// viewer_has_subscribed +const community = gql` + query ($raw: String) { + community(raw: $raw) { id + logo + title + desc + subscribersCount } } ` const schema = { - simpleMutation, - simpleQuery, + community, } export default schema diff --git a/src/containers/editor/ArticleEditor/store.ts b/src/containers/editor/ArticleEditor/store.ts index 7f9b60673..28178f5dc 100755 --- a/src/containers/editor/ArticleEditor/store.ts +++ b/src/containers/editor/ArticleEditor/store.ts @@ -3,14 +3,17 @@ * */ -import { types as T, getParent, Instance } from 'mobx-state-tree' +import { types as T, Instance } from 'mobx-state-tree' import { values } from 'ramda' -import { markStates } from '@/utils/mobx' +import type { TCommunity } from '@/spec' +import { markStates, toJS } from '@/utils/mobx' +import { Community } from '@/model/Community' import { STEP } from './constant' const ArticleEditor = T.model('ArticleEditor', { + community: T.optional(Community, {}), step: T.optional(T.enumeration(values(STEP)), STEP.EDIT), showSubTitle: T.optional(T.boolean, false), }) @@ -18,9 +21,12 @@ const ArticleEditor = T.model('ArticleEditor', { // get root() { // return getParent(self) // }, + get communityData(): TCommunity { + return toJS(self.community) + }, })) .actions((self) => ({ - mark(sobj) { + mark(sobj: Record<string, unknown>): void { markStates(sobj, self) }, })) diff --git a/src/containers/editor/ArticleEditor/styles/community_badge.ts b/src/containers/editor/ArticleEditor/styles/community_badge.ts index 5322ef81f..cef765738 100644 --- a/src/containers/editor/ArticleEditor/styles/community_badge.ts +++ b/src/containers/editor/ArticleEditor/styles/community_badge.ts @@ -2,8 +2,9 @@ import styled from 'styled-components' import css from '@/utils/css' import { theme } from '@/utils/themes' -import Img from '@/Img' +// import Img from '@/Img' import ArrowSVG from '@/icons/ArrowSolid' +import CommunityFaceLogo from '@/components/CommunityFaceLogo' export const Wrapper = styled.div` ${css.flex('justify-center')}; @@ -15,7 +16,7 @@ export const BadgeWrapper = styled.div` ${css.flex('align-center')}; padding-bottom: 33px; ` -export const Logo = styled(Img)` +export const Logo = styled(CommunityFaceLogo)` ${css.size(14)}; margin-right: 8px; margin-left: 2px; diff --git a/src/containers/editor/RichEditor/index.tsx b/src/containers/editor/RichEditor/index.tsx index 68c1234b6..f880952ad 100755 --- a/src/containers/editor/RichEditor/index.tsx +++ b/src/containers/editor/RichEditor/index.tsx @@ -5,8 +5,11 @@ */ import dynamic from 'next/dynamic' +import { LavaLampLoading } from '@/components/dynamic' export const RichEditor = dynamic(() => import('./RealEditor'), { + /* eslint-disable react/display-name */ + loading: () => <LavaLampLoading top={20} bottom={100} />, ssr: false, }) diff --git a/src/pages/editor.tsx b/src/pages/editor.tsx index f3ded0114..04008b7dd 100755 --- a/src/pages/editor.tsx +++ b/src/pages/editor.tsx @@ -1,3 +1,4 @@ +import { GetServerSideProps } from 'next' import { Provider } from 'mobx-react' import { METRIC } from '@/constant' import { articleEditorSEO } from '@/utils' @@ -6,6 +7,17 @@ import { useStore } from '@/stores/init' import GlobalLayout from '@/containers/layout/GlobalLayout' import ArticleEditor from '@/containers/editor/ArticleEditor' +/** + * TODO: know-why + * + * 即使不需要这里的数据初始化 store, 这里也必须要放一个 getserverSideProps, + * 否则 Provider 在 URL 中包含 param 的时候会报错,十分诡异 。。 + * 大概率是 mobx-react 的问题, 真尼玛坑爹。 + */ +export const getServerSideProps: GetServerSideProps = async () => { + return { props: { errorCode: null } } +} + export const EditorPage = (props) => { const store = useStore(props) const seoConfig = articleEditorSEO() From 5a01b9de095b29fadb71e6acde7ce47daada0ad0 Mon Sep 17 00:00:00 2001 From: mydearxym <mydearxym@qq.com> Date: Mon, 11 Oct 2021 16:32:56 +0800 Subject: [PATCH 02/30] refactor(workflow): create route adjust --- server/routes.js | 6 +- src/containers/editor/RichEditor/index.tsx | 2 +- src/pages/create/article.js | 120 --------------------- src/pages/{editor.tsx => create/post.tsx} | 35 +++++- 4 files changed, 36 insertions(+), 127 deletions(-) delete mode 100755 src/pages/create/article.js rename src/pages/{editor.tsx => create/post.tsx} (55%) diff --git a/server/routes.js b/server/routes.js index dced7a289..72fb0ed17 100644 --- a/server/routes.js +++ b/server/routes.js @@ -108,9 +108,9 @@ router.route('/create/community').get((req, res) => { return renderAndCache({ req, res, page: '/create/community' }) }) -// 创建新内容 -router.route('/create/article').get((req, res) => { - return renderAndCache({ req, res, page: '/create/article' }) +// 创建新帖子 +router.route('/create/post').get((req, res) => { + return renderAndCache({ req, res, page: '/create/post' }) }) // 创建新博客 diff --git a/src/containers/editor/RichEditor/index.tsx b/src/containers/editor/RichEditor/index.tsx index f880952ad..c38110dcb 100755 --- a/src/containers/editor/RichEditor/index.tsx +++ b/src/containers/editor/RichEditor/index.tsx @@ -9,7 +9,7 @@ import { LavaLampLoading } from '@/components/dynamic' export const RichEditor = dynamic(() => import('./RealEditor'), { /* eslint-disable react/display-name */ - loading: () => <LavaLampLoading top={20} bottom={100} />, + loading: () => <LavaLampLoading top={20} bottom={100} left={20} />, ssr: false, }) diff --git a/src/pages/create/article.js b/src/pages/create/article.js deleted file mode 100755 index bb93cda7d..000000000 --- a/src/pages/create/article.js +++ /dev/null @@ -1,120 +0,0 @@ -/* - this page is for /create/article - */ -import React from 'react' -import { Provider } from 'mobx-react' -import { merge } from 'ramda' - -import { SITE_URL } from '@/config' -import { METRIC } from '@/constant' - -import { useStore } from '@/stores/init' - -import { - getJwtToken, - makeGQClient, - queryStringToJSON, - nilOrEmpty, - ssrRescue, - ssrParseURL, - parseTheme, -} from '@/utils' - -import GlobalLayout from '@/containers/layout/GlobalLayout' -import ArticleEditor from '@/containers/editor/ArticleEditor' - -import { P } from '@/schemas' - -const fetchData = async (props, opt) => { - const { realname } = merge({ realname: true }, opt) - - const token = realname ? getJwtToken(props) : null - const gqClient = makeGQClient(token) - const userHasLogin = nilOrEmpty(token) === false - const { subPath } = ssrParseURL(props.req) - const category = subPath !== '' ? subPath : 'pl' - - const filter = { ...queryStringToJSON(props.req.url, { pagi: 'number' }) } - - const sessionState = gqClient.request(P.sessionState) - const pagedCommunities = gqClient.request(P.pagedCommunities, { - filter: { ...filter, category }, - userHasLogin, - }) - const pagedCategories = gqClient.request(P.pagedCategories, { filter }) - - const subscribedCommunities = gqClient.request(P.subscribedCommunities, { - filter: { - page: 1, - size: 30, - }, - }) - - return { - category, - ...(await sessionState), - ...(await pagedCategories), - ...(await pagedCommunities), - ...(await subscribedCommunities), - } -} - -export const getServerSideProps = async (props) => { - let resp - try { - resp = await fetchData(props) - } catch ({ response: { errors } }) { - if (ssrRescue.hasLoginError(errors)) { - resp = await fetchData(props, { tokenExpired: true }) - } - } - - const { - // category, - sessionState, - pagedCategories, - pagedCommunities, - subscribedCommunities, - } = resp - - const initProps = { - theme: { - curTheme: parseTheme(sessionState), - }, - account: { - user: sessionState.user || {}, - isValidSession: sessionState.isValid, - userSubscribedCommunities: subscribedCommunities, - }, - exploreContent: { - pagedCommunities, - pagedCategories, - }, - } - - return { props: { errorCode: null, ...initProps } } -} - -const CreateArticlePage = (props) => { - const store = useStore(props) - - const seoConfig = { - url: `${SITE_URL}/create/article`, - title: '发帖 | coderplanets', - description: '发布帖子', - } - - return ( - <Provider store={store}> - <GlobalLayout - metric={METRIC.ARTICLE_EDITOR} - seoConfig={seoConfig} - noFooter - > - <ArticleEditor /> - </GlobalLayout> - </Provider> - ) -} - -export default CreateArticlePage diff --git a/src/pages/editor.tsx b/src/pages/create/post.tsx similarity index 55% rename from src/pages/editor.tsx rename to src/pages/create/post.tsx index 04008b7dd..1db1f0f56 100755 --- a/src/pages/editor.tsx +++ b/src/pages/create/post.tsx @@ -1,12 +1,28 @@ import { GetServerSideProps } from 'next' import { Provider } from 'mobx-react' import { METRIC } from '@/constant' -import { articleEditorSEO } from '@/utils' +import { + articleEditorSEO, + ssrBaseStates, + ssrRescue, + ssrFetchPrepare, +} from '@/utils' +import { P } from '@/schemas' import { useStore } from '@/stores/init' import GlobalLayout from '@/containers/layout/GlobalLayout' import ArticleEditor from '@/containers/editor/ArticleEditor' +const fetchData = async (context, opt = {}) => { + const { gqClient } = ssrFetchPrepare(context, opt) + + const sessionState = gqClient.request(P.sessionState) + + return { + ...(await sessionState), + } +} + /** * TODO: know-why * @@ -14,8 +30,21 @@ import ArticleEditor from '@/containers/editor/ArticleEditor' * 否则 Provider 在 URL 中包含 param 的时候会报错,十分诡异 。。 * 大概率是 mobx-react 的问题, 真尼玛坑爹。 */ -export const getServerSideProps: GetServerSideProps = async () => { - return { props: { errorCode: null } } +export const getServerSideProps: GetServerSideProps = async (context) => { + let resp + try { + resp = await fetchData(context) + } catch ({ response: { errors } }) { + if (ssrRescue.hasLoginError(errors)) { + resp = await fetchData(context, { tokenExpired: true }) + } + } + + const initProps = { + ...ssrBaseStates(resp), + } + + return { props: { errorCode: null, ...initProps } } } export const EditorPage = (props) => { From b7800f22e2b11428533a4adfab4b453807d21fe8 Mon Sep 17 00:00:00 2001 From: mydearxym <mydearxym@qq.com> Date: Mon, 11 Oct 2021 18:19:00 +0800 Subject: [PATCH 03/30] refactor(publish-post): wip --- .../ArticleEditToolbar/CopyrightSelector.js | 87 ------------------- src/components/ArticleEditToolbar/index.js | 71 --------------- .../styles/copyright_selector.ts | 51 ----------- .../ArticleEditToolbar/styles/index.ts | 63 -------------- .../ArticleEditToolbar/tests/index.test.ts | 10 --- .../editor/ArticleEditor/Footer.tsx | 14 ++- .../editor/ArticleEditor/PublishRules.tsx | 5 +- .../editor/ArticleEditor/TitleInput/index.tsx | 14 ++- src/containers/editor/ArticleEditor/index.tsx | 6 +- src/containers/editor/ArticleEditor/logic.ts | 5 ++ src/containers/editor/ArticleEditor/store.ts | 8 ++ .../ArticleEditor/styles/community_badge.ts | 1 + .../editor/ArticleEditor/styles/footer.ts | 6 +- .../editor/RichEditor/RealEditor.tsx | 4 +- 14 files changed, 49 insertions(+), 296 deletions(-) delete mode 100755 src/components/ArticleEditToolbar/CopyrightSelector.js delete mode 100755 src/components/ArticleEditToolbar/index.js delete mode 100755 src/components/ArticleEditToolbar/styles/copyright_selector.ts delete mode 100755 src/components/ArticleEditToolbar/styles/index.ts delete mode 100755 src/components/ArticleEditToolbar/tests/index.test.ts diff --git a/src/components/ArticleEditToolbar/CopyrightSelector.js b/src/components/ArticleEditToolbar/CopyrightSelector.js deleted file mode 100755 index 38f781b56..000000000 --- a/src/components/ArticleEditToolbar/CopyrightSelector.js +++ /dev/null @@ -1,87 +0,0 @@ -import React from 'react' -import { path, reject, find, propEq } from 'ramda' -// import T from 'prop-types' - -import { ICON, ICON_CMD } from '@/config' -import { THREAD } from '@/constant' - -import Tooltip from '@/components/Tooltip' - -import { - Wrapper, - Selector, - CheckIcon, - CheckText, - ReprintIcon, - ReprintWrapper, - CopyRightText, - MoreIcon, -} from './styles/copyright_selector' - -const FullOptions = [ - { - title: '原创', - value: 'original', - }, - { - title: '转载', - value: 'reprint', - }, - { - title: '翻译', - value: 'translate', - }, -] - -const getOptions = (thread) => { - switch (thread) { - case THREAD.JOB: - return reject((o) => o.value === 'translate', FullOptions) - - case THREAD.RADAR: - return reject((o) => o.value === 'original', FullOptions) - - default: - return FullOptions - } -} - -const getCpTitle = (cptype) => - path(['title'], find(propEq('value', cptype), FullOptions)) - -const CopyrightContent = ({ active, thread, onCopyrightChange }) => ( - <Wrapper> - {getOptions(thread).map((opt) => ( - <Selector key={opt.value} onClick={() => onCopyrightChange(opt.value)}> - <CheckText>{opt.title}</CheckText> - <CheckIcon - src={`${ICON_CMD}/check2.svg`} - active={active === opt.value} - /> - </Selector> - ))} - </Wrapper> -) - -const CopyrightSelector = ({ copyRight, thread, onCopyrightChange }) => { - return ( - <Tooltip - content={ - <CopyrightContent - active={copyRight} - thread={thread} - onCopyrightChange={onCopyrightChange} - /> - } - placement="bottom" - > - <ReprintWrapper> - <ReprintIcon src={`${ICON_CMD}/${copyRight}.svg`} /> - <CopyRightText>{getCpTitle(copyRight)}</CopyRightText> - <MoreIcon src={`${ICON}/shape/arrow-solid.svg`} /> - </ReprintWrapper> - </Tooltip> - ) -} - -export default React.memo(CopyrightSelector) diff --git a/src/components/ArticleEditToolbar/index.js b/src/components/ArticleEditToolbar/index.js deleted file mode 100755 index a343ee1cc..000000000 --- a/src/components/ArticleEditToolbar/index.js +++ /dev/null @@ -1,71 +0,0 @@ -/* - * - * ArticleEditToolbar - * - */ - -import React from 'react' -import T from 'prop-types' - -import { SITE_URL } from '@/config' -import { THREAD } from '@/constant' -import { buildLog } from '@/utils/logger' - -import CopyrightSelector from './CopyrightSelector' - -import { - Wrapper, - LinkLabel, - LinkInput, - SourceLink, - CopyRightWrapper, -} from './styles' - -/* eslint-disable-next-line */ -const log = buildLog('c:ArticleEditToolbar:index') - -const ArticleEditToolbar = ({ - onLinkAddrChange, - onCopyrightChange, - thread, - editData: { copyRight, linkAddr }, -}) => ( - <Wrapper> - <CopyRightWrapper> - <CopyrightSelector - copyRight={copyRight} - thread={thread} - onCopyrightChange={onCopyrightChange} - /> - </CopyRightWrapper> - - {copyRight !== 'original' && ( - <SourceLink> - <LinkLabel>原文地址</LinkLabel> - <LinkInput - placeholder={`请填写url地址, 比如: ${SITE_URL}`} - value={linkAddr} - onChange={onLinkAddrChange} - /> - </SourceLink> - )} - </Wrapper> -) - -ArticleEditToolbar.propTypes = { - thread: T.oneOf([THREAD.POST, THREAD.JOB]), - editData: T.shape({ - copyRight: T.string, - linkAddr: T.string, - }).isRequired, - onLinkAddrChange: T.func, - onCopyrightChange: T.func, -} - -ArticleEditToolbar.defaultProps = { - thread: THREAD.POST, - onLinkAddrChange: log, - onCopyrightChange: log, -} - -export default React.memo(ArticleEditToolbar) diff --git a/src/components/ArticleEditToolbar/styles/copyright_selector.ts b/src/components/ArticleEditToolbar/styles/copyright_selector.ts deleted file mode 100755 index f731b29f6..000000000 --- a/src/components/ArticleEditToolbar/styles/copyright_selector.ts +++ /dev/null @@ -1,51 +0,0 @@ -import styled from 'styled-components' - -import type { TActive } from '@/spec' -import { theme } from '@/utils/themes' -import css from '@/utils/css' -import Img from '@/Img' - -export const Wrapper = styled.div` - padding-left: 5px; -` -export const Selector = styled.div` - ${css.flex('align-center')}; - margin-bottom: 5px; - &:hover { - cursor: pointer; - color: ${theme('editor.title')}; - } -` -export const CheckIcon = styled(Img)<TActive>` - fill: ${theme('editor.content')}; - ${css.size(18)}; - margin-top: 2px; - margin-left: 3px; - visibility: ${({ active }) => (active ? 'visible' : 'hidden')}; -` -export const CheckText = styled.div` - color: ${theme('editor.content')}; -` -export const CopyRightText = styled.div` - font-size: 13px; -` -export const ReprintWrapper = styled.div` - ${css.flex('align-center')}; - color: ${theme('editor.content')}; - cursor: pointer; -` -export const ReprintIcon = styled(Img)` - fill: ${theme('editor.content')}; - ${css.size(13)}; - margin-right: 5px; -` -export const MoreIcon = styled(Img)` - ${css.size(13)}; - margin-left: 3px; - fill: ${theme('editor.placeholder')}; - &:hover { - cursor: pointer; - } - - transform: rotate(90deg); -` diff --git a/src/components/ArticleEditToolbar/styles/index.ts b/src/components/ArticleEditToolbar/styles/index.ts deleted file mode 100755 index 02c94a324..000000000 --- a/src/components/ArticleEditToolbar/styles/index.ts +++ /dev/null @@ -1,63 +0,0 @@ -import styled from 'styled-components' - -import Input from '@/components/Input' - -// import Img from '@/Img' -import { theme } from '@/utils/themes' -import css from '@/utils/css' - -export const Wrapper = styled.div` - ${css.flex('align-center')}; - width: 100%; -` -export const CopyRightWrapper = styled.div` - ${css.flex()}; -` -export const SourceLink = styled.div` - ${css.flex('align-center')}; - width: 400px; - margin-left: 16px; -` -export const LinkInput = styled(Input)` - height: 20px; - line-height: 20px; - width: 300px; - font-size: 12px; - margin-top: -1px; - background: transparent; - padding-left: 2px; - border-radius: 0; - color: ${theme('editor.title')}; - border: none; - border-bottom: 1px solid transparent; - - ::placeholder { - color: ${theme('editor.placeholder')}; - } - - text-align: left; - &:hover { - border-color: ${theme('editor.headerBg')}; - border-bottom: 1px solid; - border-bottom-color: ${theme('editor.border')}; - color: ${theme('editor.title')}; - } - &:focus { - border-color: ${theme('editor.headerBg')}; - box-shadow: none; - border-bottom: 1px solid; - border-bottom-color: ${theme('editor.placeholder')}; - color: ${theme('editor.title')}; - text-align: left; - } -` -export const LinkLabel = styled.div` - font-size: 12px; - width: 60px; - margin-top: 2px; - color: ${theme('editor.placeholder')}; - ${SourceLink}:hover & { - color: ${theme('editor.title')}; - } - transition: color 0.2s; -` diff --git a/src/components/ArticleEditToolbar/tests/index.test.ts b/src/components/ArticleEditToolbar/tests/index.test.ts deleted file mode 100755 index 06792d57c..000000000 --- a/src/components/ArticleEditToolbar/tests/index.test.ts +++ /dev/null @@ -1,10 +0,0 @@ -// import React from 'react' -// import { shallow } from 'enzyme' - -// import ArticleEditToolbar from '../index' - -describe('TODO <ArticleEditToolbar />', () => { - it('Expect to have unit tests specified', () => { - expect(true).toEqual(true) - }) -}) diff --git a/src/containers/editor/ArticleEditor/Footer.tsx b/src/containers/editor/ArticleEditor/Footer.tsx index 5728744a6..7a02e855a 100644 --- a/src/containers/editor/ArticleEditor/Footer.tsx +++ b/src/containers/editor/ArticleEditor/Footer.tsx @@ -1,18 +1,24 @@ import { FC, memo } from 'react' -import Checker from '@/components/Checker' import YesOrNoButtons from '@/components/Buttons/YesOrNoButtons' +import Copyright from '@/components/Copyright' +import Checker from '@/components/Checker' +import { SpaceGrow } from '@/components/Common' import { Wrapper, ArticleFooter, PublishFooter } from './styles/footer' const Footer: FC = () => { return ( <Wrapper> - <ArticleFooter>社区 / Tags etc ...</ArticleFooter> - <PublishFooter> + <ArticleFooter> + <div>社区 / Tags etc ...</div> <Checker size="medium" dimWhenIdle> - 求助 / 问答类 + 求助 / 提问 </Checker> + </ArticleFooter> + <PublishFooter> + <Copyright mode="editable" /> + <SpaceGrow /> <div> <YesOrNoButtons cancelText="取消" diff --git a/src/containers/editor/ArticleEditor/PublishRules.tsx b/src/containers/editor/ArticleEditor/PublishRules.tsx index a40e69bcf..d2d472fcb 100644 --- a/src/containers/editor/ArticleEditor/PublishRules.tsx +++ b/src/containers/editor/ArticleEditor/PublishRules.tsx @@ -9,8 +9,9 @@ const PublishRules: FC = () => { <Title>发帖须知
  • 友善是一切有意义讨论的基础和前提。Don't be an asshole.
  • - 如果是求助,问答类帖子,请务必提供上下文、需求场景等必要信息。”如何看待 - xxx” / “xx 和 xx 哪个更 xx” 等炸鱼问题在这里不受欢迎。 + 如果是寻求解答,请务必提供上下文、需求场景等必要信息, 并勾选发布上方的 + “求助 / 提问” 选框。这里崇尚具体实际的问题,请不要使用 “如何看待xxx” + 开头。
  • 如果不确定内容该发布到哪个社区,可以先发布到 /draft,并 @ diff --git a/src/containers/editor/ArticleEditor/TitleInput/index.tsx b/src/containers/editor/ArticleEditor/TitleInput/index.tsx index c00a9c562..e0beb6bc6 100644 --- a/src/containers/editor/ArticleEditor/TitleInput/index.tsx +++ b/src/containers/editor/ArticleEditor/TitleInput/index.tsx @@ -1,11 +1,21 @@ import { FC, memo } from 'react' import { Wrapper, Inputer } from '../styles/title_input' +import { editOnChange } from '../logic' -const TitleInput: FC = () => { +type TProps = { + title: string +} + +const TitleInput: FC = ({ title }) => { return ( - + editOnChange(e, 'title')} + /> ) } diff --git a/src/containers/editor/ArticleEditor/index.tsx b/src/containers/editor/ArticleEditor/index.tsx index 969a8b01e..b735db203 100755 --- a/src/containers/editor/ArticleEditor/index.tsx +++ b/src/containers/editor/ArticleEditor/index.tsx @@ -39,14 +39,14 @@ const ArticleEditorContainer: FC = ({ metric = METRIC.ARTICLE_EDITOR, }) => { useInit(store) - const { communityData } = store + const { title, communityData } = store return ( - - + + console.log('rich: ', data)} />
    diff --git a/src/containers/editor/ArticleEditor/logic.ts b/src/containers/editor/ArticleEditor/logic.ts index d4d485137..2730778e2 100755 --- a/src/containers/editor/ArticleEditor/logic.ts +++ b/src/containers/editor/ArticleEditor/logic.ts @@ -4,6 +4,7 @@ import { useEffect } from 'react' import { buildLog } from '@/utils/logger' import asyncSuit from '@/utils/async' import { getParameterByName } from '@/utils/route' +import { updateEditing } from '@/utils/mobx' import type { TStore } from './store' import { STEP } from './constant' @@ -34,6 +35,10 @@ export const loadCommunity = (communityRaw: string): void => { sr71$.query(S.community, { raw: communityRaw.toLowerCase() }) } +export const editOnChange = (e, key: string): void => { + updateEditing(store, key, e) +} + // ############################### // init & uninit handlers // ############################### diff --git a/src/containers/editor/ArticleEditor/store.ts b/src/containers/editor/ArticleEditor/store.ts index 28178f5dc..f24823f72 100755 --- a/src/containers/editor/ArticleEditor/store.ts +++ b/src/containers/editor/ArticleEditor/store.ts @@ -13,6 +13,10 @@ import { Community } from '@/model/Community' import { STEP } from './constant' const ArticleEditor = T.model('ArticleEditor', { + title: T.optional(T.string, ''), + body: T.optional(T.string, '{}'), + linkAddr: T.optional(T.string, ''), + copyRight: T.optional(T.string, ''), community: T.optional(Community, {}), step: T.optional(T.enumeration(values(STEP)), STEP.EDIT), showSubTitle: T.optional(T.boolean, false), @@ -26,6 +30,10 @@ const ArticleEditor = T.model('ArticleEditor', { }, })) .actions((self) => ({ + updateEditing(sobj): void { + const slf = self as TStore + slf.mark(sobj) + }, mark(sobj: Record): void { markStates(sobj, self) }, diff --git a/src/containers/editor/ArticleEditor/styles/community_badge.ts b/src/containers/editor/ArticleEditor/styles/community_badge.ts index cef765738..6ba043a04 100644 --- a/src/containers/editor/ArticleEditor/styles/community_badge.ts +++ b/src/containers/editor/ArticleEditor/styles/community_badge.ts @@ -35,6 +35,7 @@ export const Intro = styled.div` export const PubHint = styled.div` color: ${theme('thread.articleDigest')}; font-size: 12px; + margin-left: 2px; opacity: 0.85; margin-bottom: 8px; ` diff --git a/src/containers/editor/ArticleEditor/styles/footer.ts b/src/containers/editor/ArticleEditor/styles/footer.ts index 36e680037..6316c2212 100644 --- a/src/containers/editor/ArticleEditor/styles/footer.ts +++ b/src/containers/editor/ArticleEditor/styles/footer.ts @@ -9,12 +9,14 @@ export const Wrapper = styled.div` padding-left: 5px; ` export const ArticleFooter = styled.div` - ${css.flex('align-center')}; - padding-bottom: 25px; + ${css.flex('align-center', 'justify-between')}; + width: 100%; border-bottom: 3px solid; border-bottom-color: #1a3a40; margin-bottom: 28px; padding-left: 27px; + padding-right: 34px; + padding-bottom: 25px; ` export const PublishFooter = styled.div` ${css.flex('align-center', 'justify-between')}; diff --git a/src/containers/editor/RichEditor/RealEditor.tsx b/src/containers/editor/RichEditor/RealEditor.tsx index 762220bf5..1a363ec13 100644 --- a/src/containers/editor/RichEditor/RealEditor.tsx +++ b/src/containers/editor/RichEditor/RealEditor.tsx @@ -25,6 +25,7 @@ const log = buildLog('C:RichEditor') type TProps = { richEditor?: TStore type?: 'article' | 'works' | 'job' | 'comment' | 'radar' + onChange?: (json) => void } const html = @@ -33,6 +34,7 @@ const html = const RichEditorContainer: FC = ({ richEditor: store, type = 'article', + onChange = log, }) => { useInit(store) @@ -42,7 +44,7 @@ const RichEditorContainer: FC = ({ - console.log('.')} /> + From 259d1e83836763e7863718e5776a301b8530c360 Mon Sep 17 00:00:00 2001 From: mydearxym Date: Mon, 11 Oct 2021 23:38:05 +0800 Subject: [PATCH 04/30] refactor(workflow): naming & new favicon --- .eslintignore | 3 ++- public/favicon.ico | Bin 5430 -> 745 bytes server/routes.js | 16 ++++++++-------- .../editor/ArticleEditor/PublishRules.tsx | 2 +- src/pages/{create => publish}/blog.tsx | 0 src/pages/{create => publish}/community.js | 0 src/pages/{create => publish}/post.tsx | 0 src/pages/{create => publish}/works.tsx | 0 utils/seo.ts | 4 ++-- 9 files changed, 13 insertions(+), 12 deletions(-) rename src/pages/{create => publish}/blog.tsx (100%) rename src/pages/{create => publish}/community.js (100%) rename src/pages/{create => publish}/post.tsx (100%) rename src/pages/{create => publish}/works.tsx (100%) diff --git a/.eslintignore b/.eslintignore index 09abc3355..099006ba9 100644 --- a/.eslintignore +++ b/.eslintignore @@ -8,4 +8,5 @@ next-env.d.ts *.png *.jpeg *.txt -*.md \ No newline at end of file +*.md +*.ico \ No newline at end of file diff --git a/public/favicon.ico b/public/favicon.ico index 3646fced4040b8624f1ae3b2d80d44b76494b547..1566d7dc76a70a8c7876084640f17a61887c0063 100755 GIT binary patch literal 745 zcmZQzU<5)11qKkwaG8mLL5zWcp)+$7d zRbnzXj_vyQ^)XQ7`OVWGpI!U*<~ET1=gZ^Q&#!;~aR2$^E1Et*8+NRJ^xz_cw%Nip zOI|*`<`9>(W8an^pB}t@abw@19ooLZYqqTZ`RSo!d{RJGZo|aBjXO3l>6p)1zTn5l z2Qp5cPaj=&Pf6dlXS0BXqoAeJqBToDzq@<;)|n5lZ_in=aOeK5+xBku&B*%s>4B`1 zmswc!t7q4^jBPJoI(q2X?iWw5zIbx=@3$ut=FPN^P26{Q$D{?bCd``=n3I=YTO)4g z_V??P!^ih@&zhQ6Q*-RpzV+MIRrYkYPM!Gh{)HJ!=WXA+`PZk1zdt{Gdga*L`xh!B zVu9q3_jiAOxc}kFm8aKEoU?oO6c{csB|(0{3>x)4UCs=ezy8NIoxAy;3ZUmk&}8PC zPk)vO9GHIVD$t$}o-U3d6?2jk5>k?maYjVkl(=DO@sUxFEpN}KIW|0Jeu#eH6Jo9T z!q$PFAn3W z3(HonSk>9Js#!Fnqbo~EBqP(gp-+#Gwdz+uLW6T-$HIkzEv?Rt8&|C0Y)Q>ZOXCYm zNaPGjNabZT<6xb$ZJSsEYuUGHv*z$^XkgMVC~0DF_DH#DxqE?TLF=bG3pT!dckty) zR>pe~q6*9#nB?O*J{(@kV7kd8e`w!^KN{aF_ Y%&l;-4Cp=e3g~wRPgg&ebxsLQ0RPyAF8}}l literal 5430 zcmeHLO-dv&6wVAX;4mpXfix~en8mDZ7#KW2yDPyH%)*7}9KaK}@D$Q#a4oYCkKjUP z)eKB8L{x;D?@L~qXDU_wXIBY!;F07@zI=JFUeckB`CvYpwKapfV}7m}vuTX!bbS9D z_$jb0U}$27$5Fk>lK1RY-ph*Fj9gpfyAgllKDf}{+s!Uf=UC^oDoB`UOv{D zegAR%(eGn#UGGPB!&uhEZ^W1QPsjH;pkfhXGC@r)a&Y2&lX#Ej+p9QFn(h~cF|<&= z{WXTh^8Laa+g}g7vCR^Ayk|PFA28lW%d(6>Dgrns`>-L-ChyzEjGCf7Na^zIrtpBx zJI-~WKY-Qh1ieMnZEC0d!CmWqM^X1>d_9#j<1la?AM2F()JSO))B5s#OZ;)#9H+kA z{U!chjXrn__n#W{HD%g=887XBQvb=HHvXwSHHl%rd_UbO;~AwN3bRk*+k?U%Jm%gy zdM>#UKXd$)UzmFa`=aa*^kEvaXQIEO@Ey^Q_>LR#GsmycAH8EGk8h!ItG_w=A%D67 z$68(4(+fViz^nMB9OT+z&6f0|_+?GUzt#Lp3OR!IXV^XP z%YaGVy!!oLtKTc@zxMrKt3QSRSSOs{%JbJ&{IR#vo)(Q&y@BR^-;LwbFaO(O2aEQx zYJKSQ{n_gKTc6uy`~5|rh{_57E>LB)cpPdAL)r25p|*gp{@WgE3pnc&tR2Y}{{afj B5-0!w diff --git a/server/routes.js b/server/routes.js index 72fb0ed17..cecd20ce4 100644 --- a/server/routes.js +++ b/server/routes.js @@ -104,23 +104,23 @@ router.route('/:community/repo/:id').get((req, res) => { }) // 创建新社区 -router.route('/create/community').get((req, res) => { - return renderAndCache({ req, res, page: '/create/community' }) +router.route('/publish/community').get((req, res) => { + return renderAndCache({ req, res, page: '/publish/community' }) }) // 创建新帖子 -router.route('/create/post').get((req, res) => { - return renderAndCache({ req, res, page: '/create/post' }) +router.route('/publish/post').get((req, res) => { + return renderAndCache({ req, res, page: '/publish/post' }) }) // 创建新博客 -router.route('/create/blog').get((req, res) => { - return renderAndCache({ req, res, page: '/create/blog' }) +router.route('/publish/blog').get((req, res) => { + return renderAndCache({ req, res, page: '/publish/blog' }) }) // 创建新作品 -router.route('/create/works').get((req, res) => { - return renderAndCache({ req, res, page: '/create/works' }) +router.route('/publish/works').get((req, res) => { + return renderAndCache({ req, res, page: '/publish/works' }) }) // 所有社区 diff --git a/src/containers/editor/ArticleEditor/PublishRules.tsx b/src/containers/editor/ArticleEditor/PublishRules.tsx index d2d472fcb..3cf50329e 100644 --- a/src/containers/editor/ArticleEditor/PublishRules.tsx +++ b/src/containers/editor/ArticleEditor/PublishRules.tsx @@ -18,7 +18,7 @@ const PublishRules: FC = () => { 相关社区的志愿者,他们很乐于给你反馈。
  • 请尊重自己和他人的时间,不要发布无意义的烂梗和 trash talk。
  • -
  • 禁止盗版,不谈国是,勿搞沸腾。
  • +
  • 严禁侵权,勿议国是,不搞沸腾。
  • 如有其他疑问或建议反馈,请发布到 /feetback#publish。
  • diff --git a/src/pages/create/blog.tsx b/src/pages/publish/blog.tsx similarity index 100% rename from src/pages/create/blog.tsx rename to src/pages/publish/blog.tsx diff --git a/src/pages/create/community.js b/src/pages/publish/community.js similarity index 100% rename from src/pages/create/community.js rename to src/pages/publish/community.js diff --git a/src/pages/create/post.tsx b/src/pages/publish/post.tsx similarity index 100% rename from src/pages/create/post.tsx rename to src/pages/publish/post.tsx diff --git a/src/pages/create/works.tsx b/src/pages/publish/works.tsx similarity index 100% rename from src/pages/create/works.tsx rename to src/pages/publish/works.tsx diff --git a/utils/seo.ts b/utils/seo.ts index 3d59ceb31..0cc790148 100644 --- a/utils/seo.ts +++ b/utils/seo.ts @@ -99,7 +99,7 @@ export const articleSEO = (thread: TThread, article: TArticle): TSEO => { export const articleEditorSEO = (): TSEO => { return { url: `${SITE_URL}/todo`, - title: '创建帖子', - description: '创建新帖子', + title: '发布帖子', + description: '发布新帖子到社区', } } From baf6ba03c3b7b14e384573c1d982db88f947951d Mon Sep 17 00:00:00 2001 From: mydearxym Date: Tue, 12 Oct 2021 11:24:23 +0800 Subject: [PATCH 05/30] refactor(workflow): wip --- .../editor/ArticleEditor/Footer.tsx | 22 ++++++++++++++++--- .../editor/ArticleEditor/PublishRules.tsx | 2 +- src/containers/editor/ArticleEditor/index.tsx | 12 ++++++---- src/containers/editor/ArticleEditor/logic.ts | 7 +++--- src/containers/editor/ArticleEditor/store.ts | 8 +++++-- .../RichEditor/Options/ArticleLayout.tsx | 2 +- src/spec/index.ts | 1 + src/spec/utils.ts | 2 ++ utils/mobx.ts | 9 ++++---- 9 files changed, 47 insertions(+), 18 deletions(-) diff --git a/src/containers/editor/ArticleEditor/Footer.tsx b/src/containers/editor/ArticleEditor/Footer.tsx index 7a02e855a..41e9c7853 100644 --- a/src/containers/editor/ArticleEditor/Footer.tsx +++ b/src/containers/editor/ArticleEditor/Footer.tsx @@ -1,23 +1,39 @@ import { FC, memo } from 'react' +import type { TCopyright } from '@/spec' import YesOrNoButtons from '@/components/Buttons/YesOrNoButtons' import Copyright from '@/components/Copyright' import Checker from '@/components/Checker' import { SpaceGrow } from '@/components/Common' import { Wrapper, ArticleFooter, PublishFooter } from './styles/footer' +import { editOnChange } from './logic' -const Footer: FC = () => { +type TProps = { + isQuestion: boolean + copyRight: string +} + +const Footer: FC = ({ isQuestion, copyRight }) => { return (
    社区 / Tags etc ...
    - + editOnChange(v, 'isQuestion')} + > 求助 / 提问
    - + editOnChange(v, 'copyRight')} + />
    {
  • 友善是一切有意义讨论的基础和前提。Don't be an asshole.
  • 如果是寻求解答,请务必提供上下文、需求场景等必要信息, 并勾选发布上方的 - “求助 / 提问” 选框。这里崇尚具体实际的问题,请不要使用 “如何看待xxx” + “求助 / 提问” 选框。这里崇尚具体实际的问题,请慎用 “如何看待 xxx” 开头。
  • diff --git a/src/containers/editor/ArticleEditor/index.tsx b/src/containers/editor/ArticleEditor/index.tsx index b735db203..8167975c2 100755 --- a/src/containers/editor/ArticleEditor/index.tsx +++ b/src/containers/editor/ArticleEditor/index.tsx @@ -22,7 +22,7 @@ import PublishRules from './PublishRules' import type { TStore } from './store' import { Wrapper, InnerWrapper, ContentWrapper } from './styles' -import { useInit } from './logic' +import { useInit, editOnChange } from './logic' /* eslint-disable-next-line */ const log = buildLog('C:ArticleEditor') @@ -39,15 +39,19 @@ const ArticleEditorContainer: FC = ({ metric = METRIC.ARTICLE_EDITOR, }) => { useInit(store) - const { title, communityData } = store + const { title, copyRight, editingData, isQuestion, communityData } = store + + console.log('editingData --> ', editingData) return ( - console.log('rich: ', data)} /> -