From aeb0a917594e72bb45162fb3d65bd7af992a6483 Mon Sep 17 00:00:00 2001 From: xieyimian Date: Sun, 15 Oct 2023 09:40:26 +0800 Subject: [PATCH] refactor(upload): improve workflow with STS tokens (#148) * refactor: wip * chore: adjust theme --- config/config.json | 14 +-- .../CommunityContent/SubscribedList/index.tsx | 5 +- src/containers/content/UserContent/schema.ts | 4 +- .../ArticleDigest/DesktopView/Header.tsx | 3 +- .../ArticleDigest/MobileView/Header.tsx | 5 +- .../editor/CommunityEditor/schema.ts | 2 +- .../DashboardThread/BasicInfo/BaseInfo.tsx | 13 ++- .../thread/DashboardThread/logic/index.ts | 5 +- .../thread/DashboardThread/schema.ts | 10 +- .../thread/DashboardThread/store/index.ts | 5 + src/containers/tool/ArticleSticker/schema.ts | 2 +- src/containers/tool/Cashier/schema.ts | 2 +- .../tool/ErrorBox/GraphqlDetail.tsx | 10 +- src/containers/tool/ErrorBox/index.tsx | 24 +++-- .../tool/ErrorBox/styles/details.ts | 6 +- src/containers/tool/ErrorBox/styles/header.ts | 2 - src/containers/tool/ErrorBox/styles/index.ts | 20 +++- src/containers/unit/Comments/schema.ts | 2 +- .../unit/ModeLine/CommunityLayout/index.tsx | 3 +- .../unit/ModeLineMenu/CommunityMenu.tsx | 4 +- src/containers/user/UserBilling/schema.ts | 2 +- src/containers/user/UserLister/schema.ts | 8 +- src/schemas/fragments/base.ts | 1 - src/schemas/pages/community.ts | 5 +- src/schemas/pages/misc.ts | 2 +- src/schemas/pages/post.ts | 5 +- src/spec/dashboard.d.ts | 11 +++ src/spec/index.ts | 2 + src/spec/utils.d.ts | 6 ++ src/stores/ViewingStore/index.ts | 15 ++- src/widgets/CommunityBadgeSelector/index.tsx | 3 +- .../HeaderLayout/CommunityBrief.tsx | 9 +- src/widgets/CommunityList/index.tsx | 4 +- src/widgets/Input/styles/textarea.ts | 2 +- src/widgets/Navigator/BriefView.tsx | 4 +- src/widgets/Navigator/CardPopover.tsx | 4 +- src/widgets/OSSUploader/PreviewBlock.tsx | 20 ++++ src/widgets/OSSUploader/constant.ts | 11 +++ src/widgets/OSSUploader/helper.ts | 87 ++++++++++++++--- src/widgets/OSSUploader/index.tsx | 94 ++++++++++++++----- src/widgets/OSSUploader/schema.ts | 18 ++++ src/widgets/OSSUploader/spec.d.ts | 6 ++ src/widgets/OSSUploader/styles/index.ts | 73 +++++++++----- .../OSSUploader/styles/preview_block.ts | 13 +++ utils/graphql.ts | 9 +- utils/helper.ts | 23 ++++- utils/index.ts | 2 +- utils/signal.ts | 1 + utils/themes/skins/night.ts | 4 +- utils/uid.ts | 2 +- 50 files changed, 444 insertions(+), 143 deletions(-) create mode 100644 src/widgets/OSSUploader/PreviewBlock.tsx create mode 100644 src/widgets/OSSUploader/constant.ts create mode 100644 src/widgets/OSSUploader/schema.ts create mode 100644 src/widgets/OSSUploader/spec.d.ts create mode 100644 src/widgets/OSSUploader/styles/preview_block.ts diff --git a/config/config.json b/config/config.json index a39914e0d..e711c0f08 100755 --- a/config/config.json +++ b/config/config.json @@ -32,13 +32,14 @@ "grey": 11 }, "//--- endpoint configs ---//": "", - "ASSETS_ENDPOINT": "https://assets.groupher.com", - "ICON": "https://assets.groupher.com/icons/static", - "ICON_BASE": "https://assets.groupher.com/icons", - "ICON_CMD": "https://assets.groupher.com/icons/cmd", - "DEFAULT_ICON": "https://assets.groupher.com/icons/cmd/cheatsheet.svg", - "DEFAULT_USER_AVATAR": "https://assets.groupher.com/icons/cmd/alien_user3.svg", + "ASSETS_ENDPOINT": "https://static.groupher.com", + "ICON": "https://static.groupher.com/icons/static", + "ICON_BASE": "https://static.groupher.com/icons", + "ICON_CMD": "https://static.groupher.com/icons/cmd", + "DEFAULT_ICON": "https://static.groupher.com/icons/cmd/cheatsheet.svg", + "DEFAULT_USER_AVATAR": "https://static.groupher.com/icons/cmd/alien_user3.svg", "GRAPHQL_ENDPOINT": "http://127.0.0.1:4001/graphiql", + "// GRAPHQL_ENDPOINT": "http://localhost:4001/graphiql", "SITE_URL": "https://groupher.com", "SITE_NAME": "Groupher", "SITE_SLOGAN": "让你的产品聆听用户的声音。互动讨论,GTD 看板,更新日志,帮助文档多合一,收集整理用户用户反馈,助你打造更好的产品。", @@ -50,7 +51,6 @@ "MENTION_USER_ADDR": "https://groupher.com/users/", "//--- contact configs ---//": "", "EMAIL_SUPPORT": "groupher@outlook.com", - "// GRAPHQL_ENDPOINT": "http://localhost:4001/graphiql", "BEIAN_ADDR": "http://beian.miit.gov.cn", "BEIAN_TEXT": "蜀ICP备17043722号-4", "/// GRAPHQL_ENDPOINT": "https://api.groupher.com/graphiql" diff --git a/src/containers/content/CommunityContent/SubscribedList/index.tsx b/src/containers/content/CommunityContent/SubscribedList/index.tsx index 5e8ecd2e3..1b5dd0761 100755 --- a/src/containers/content/CommunityContent/SubscribedList/index.tsx +++ b/src/containers/content/CommunityContent/SubscribedList/index.tsx @@ -7,6 +7,7 @@ import { FC, memo, Fragment, useState } from 'react' import type { TCommunity } from '@/spec' import { ICON } from '@/config' import { HCN } from '@/constant/name' +import { assetSrc } from '@/helper' import { changeToCommunity } from '@/signal' import { buildLog } from '@/logger' @@ -55,11 +56,11 @@ const SubscribedList: FC = ({ community, communities }) => { 首页 {communities.slice(0, 15).map((community) => ( - + {community.slug === activeCommunity.slug && } - + changeToCommunity(community.slug)} diff --git a/src/containers/content/UserContent/schema.ts b/src/containers/content/UserContent/schema.ts index 351d67d31..7a564ebc9 100755 --- a/src/containers/content/UserContent/schema.ts +++ b/src/containers/content/UserContent/schema.ts @@ -26,7 +26,7 @@ const user = gql` ` const editableCommunities = gql` - query ($login: String, $filter: PagedFilter!) { + query ($login: String, $filter: PagiFilter!) { editableCommunities(login: $login, filter: $filter) { entries { ${F.community} @@ -37,7 +37,7 @@ const editableCommunities = gql` ` const pagedPublishedWorks = ` - query($login: String!, $filter: PagedFilter!) { + query($login: String!, $filter: PagiFilter!) { pagedPublishedWorks(login: $login, filter: $filter) { entries { id diff --git a/src/containers/digest/ArticleDigest/DesktopView/Header.tsx b/src/containers/digest/ArticleDigest/DesktopView/Header.tsx index 228b55964..9c51f0201 100644 --- a/src/containers/digest/ArticleDigest/DesktopView/Header.tsx +++ b/src/containers/digest/ArticleDigest/DesktopView/Header.tsx @@ -2,6 +2,7 @@ import { FC } from 'react' import { observer } from 'mobx-react' import type { TMetric } from '@/spec' +import { assetSrc } from '@/helper' import { ANCHOR } from '@/constant/dom' import { ROUTE } from '@/constant/route' @@ -33,7 +34,7 @@ const Header: FC<TProps> = ({ metric }) => { <Wrapper id={ANCHOR.GLOBAL_HEADER_ID}> <InnerWrapper> <Community> - <CommunityLogo src={community.logo} /> + <CommunityLogo src={assetSrc(community.logo)} /> <CommunityTitle>{community.title}</CommunityTitle> </Community> diff --git a/src/containers/digest/ArticleDigest/MobileView/Header.tsx b/src/containers/digest/ArticleDigest/MobileView/Header.tsx index b72935160..ec7ee1d41 100644 --- a/src/containers/digest/ArticleDigest/MobileView/Header.tsx +++ b/src/containers/digest/ArticleDigest/MobileView/Header.tsx @@ -1,9 +1,10 @@ import { FC } from 'react' import { observer } from 'mobx-react' -import type { TCommunity, TMetric } from '@/spec' +import type { TMetric } from '@/spec' import useViewingCommunity from '@/hooks/useViewingCommunity' +import { assetSrc } from '@/helper' import { ANCHOR } from '@/constant/dom' import { ROUTE } from '@/constant/route' @@ -34,7 +35,7 @@ const Header: FC<TProps> = ({ metric }) => { <Wrapper id={ANCHOR.GLOBAL_HEADER_ID}> <InnerWrapper> <Community> - <CommunityLogo src={community.logo} /> + <CommunityLogo src={assetSrc(community.logo)} /> <CommunityTitle>{community.title}</CommunityTitle> </Community> diff --git a/src/containers/editor/CommunityEditor/schema.ts b/src/containers/editor/CommunityEditor/schema.ts index 0f24e93b8..bb3147e0d 100755 --- a/src/containers/editor/CommunityEditor/schema.ts +++ b/src/containers/editor/CommunityEditor/schema.ts @@ -49,7 +49,7 @@ const unsubscribeCommunity = gql` } ` const pagedCategories = gql` - query ($filter: PagedFilter!) { + query ($filter: PagiFilter!) { pagedCategories(filter: $filter) { entries { id diff --git a/src/containers/thread/DashboardThread/BasicInfo/BaseInfo.tsx b/src/containers/thread/DashboardThread/BasicInfo/BaseInfo.tsx index f19acf36c..326eae50a 100644 --- a/src/containers/thread/DashboardThread/BasicInfo/BaseInfo.tsx +++ b/src/containers/thread/DashboardThread/BasicInfo/BaseInfo.tsx @@ -30,22 +30,27 @@ type TProps = { } const BasicInfo: FC<TProps> = ({ testid = 'basic-info', settings, touched }) => { - const { saving, desc, title, introduction } = settings + const { saving, desc, title, introduction, logo } = settings return ( <Wrapper> <Title>favicon - + 上传 favicon, 仅支持 ico 格式,最大 10 KB。可选。
- LOGO - + edit('', 'logo')} + onUploadDone={(v) => edit(v, 'logo')} + > diff --git a/src/containers/thread/DashboardThread/logic/index.ts b/src/containers/thread/DashboardThread/logic/index.ts index 09accb586..e10014148 100644 --- a/src/containers/thread/DashboardThread/logic/index.ts +++ b/src/containers/thread/DashboardThread/logic/index.ts @@ -502,7 +502,10 @@ const _handleError = () => { const DataSolver = [ { match: asyncRes('updateDashboardBaseInfo'), - action: () => _handleDone(), + action: ({ updateDashboardBaseInfo }) => { + store.updateViewingCommunity({ ...updateDashboardBaseInfo }) + _handleDone() + }, }, { match: asyncRes('updateDashboardMediaReports'), diff --git a/src/containers/thread/DashboardThread/schema.ts b/src/containers/thread/DashboardThread/schema.ts index 086f0249c..81c4cbabb 100755 --- a/src/containers/thread/DashboardThread/schema.ts +++ b/src/containers/thread/DashboardThread/schema.ts @@ -4,7 +4,7 @@ import { P, F } from '@/schemas' const { pagedPosts, pagedChangelogs } = P const communityBaseInfo = gql` - query community($slug: String, $incViews: Boolean) { + query community($slug: String!, $incViews: Boolean) { community(slug: $slug, incViews: $incViews) { dashboard { baseInfo { @@ -31,7 +31,7 @@ const communityBaseInfo = gql` ` const communitySocialLinks = gql` - query community($slug: String, $incViews: Boolean) { + query community($slug: String!, $incViews: Boolean) { community(slug: $slug, incViews: $incViews) { dashboard { baseInfo { @@ -71,6 +71,8 @@ const updateDashboardBaseInfo = gql` techstack: $techstack ) { title + logo + favicon } } ` @@ -271,7 +273,7 @@ const updateDashboardFaqs = gql` ` const updateModerators = gql` - query community($slug: String, $incViews: Boolean) { + query community($slug: String!, $incViews: Boolean) { community(slug: $slug, incViews: $incViews) { moderators { role @@ -288,7 +290,7 @@ const updateModerators = gql` ` const communityOverview = gql` - query community($slug: String, $incViews: Boolean) { + query community($slug: String!, $incViews: Boolean) { community(slug: $slug, incViews: $incViews) { views subscribersCount diff --git a/src/containers/thread/DashboardThread/store/index.ts b/src/containers/thread/DashboardThread/store/index.ts index 526466d07..562df20fd 100644 --- a/src/containers/thread/DashboardThread/store/index.ts +++ b/src/containers/thread/DashboardThread/store/index.ts @@ -839,6 +839,11 @@ const DashboardThread = T.model('DashboardThread', { // slf.mark({ demoAlertEnable: true }) }, + updateViewingCommunity(args: TCommunity): void { + const root = getParent(self) as TRootStore + root.viewing.updateViewingCommunity(args) + }, + _findTagIdx(): number { const slf = self as TStore diff --git a/src/containers/tool/ArticleSticker/schema.ts b/src/containers/tool/ArticleSticker/schema.ts index 7792eac42..049ab43a8 100755 --- a/src/containers/tool/ArticleSticker/schema.ts +++ b/src/containers/tool/ArticleSticker/schema.ts @@ -2,7 +2,7 @@ import { F } from '@/schemas' // const pagedCommentsParticipants = gql` -// query($id: ID!, $thread: Thread, $filter: PagedFilter!) { +// query($id: ID!, $thread: Thread, $filter: PagiFilter!) { // pagedCommentsParticipants(id: $id, thread: $thread, filter: $filter) { // entries { // ${F.author} diff --git a/src/containers/tool/Cashier/schema.ts b/src/containers/tool/Cashier/schema.ts index 98f9b07f5..a94f8f33e 100755 --- a/src/containers/tool/Cashier/schema.ts +++ b/src/containers/tool/Cashier/schema.ts @@ -24,7 +24,7 @@ const createBill = gql` } ` const pagedBillRecords = gql` - query ($filter: PagedFilter!) { + query ($filter: PagiFilter!) { pagedBillRecords(filter: $filter) { entries { id diff --git a/src/containers/tool/ErrorBox/GraphqlDetail.tsx b/src/containers/tool/ErrorBox/GraphqlDetail.tsx index 943be9311..26a528441 100755 --- a/src/containers/tool/ErrorBox/GraphqlDetail.tsx +++ b/src/containers/tool/ErrorBox/GraphqlDetail.tsx @@ -54,12 +54,10 @@ const CustomDetails = ({ errors }) => ( <> {!nilOrEmpty(errors) && errors.map((item) => ( -
- - 错误码: {item.code} - {item.message} - -
+ + 错误: {item.code} + {item.message} + ))} ) diff --git a/src/containers/tool/ErrorBox/index.tsx b/src/containers/tool/ErrorBox/index.tsx index c6809f4da..f006c3673 100755 --- a/src/containers/tool/ErrorBox/index.tsx +++ b/src/containers/tool/ErrorBox/index.tsx @@ -17,7 +17,15 @@ import { SpaceGrow } from '@/widgets/Common' import Details from './Details' -import { Wrapper, WarningIcon, ResetButton, LearnMoreButton, MoreLink } from './styles' +import { + Wrapper, + IconWrapper, + WarningIcon, + ResetButton, + LearnMoreButton, + MoreLink, + ButtonGroup, +} from './styles' import { useInit, onClose } from './logic' /* eslint-disable-next-line */ @@ -47,7 +55,9 @@ const ErrorBoxContainer: FC = ({ errorBox: store }) => { return ( - + + +
= ({ errorBox: store }) => { customError={customErrorData} /> - - 报告问题 - - onClose()}>关闭 + + + 报告问题 + + onClose()}>关闭 + ) } diff --git a/src/containers/tool/ErrorBox/styles/details.ts b/src/containers/tool/ErrorBox/styles/details.ts index fdb175449..915b2a7a0 100755 --- a/src/containers/tool/ErrorBox/styles/details.ts +++ b/src/containers/tool/ErrorBox/styles/details.ts @@ -8,13 +8,15 @@ export const Wrapper = styled.div` font-size: 13px; ` export const TitleWrapper = styled.div` - ${css.row('align-center')}; + ${css.row('align-start')}; ` export const Title = styled.div` color: ${theme('baseColor.red')}; - opacity: 0.8; + font-weight: bold; ` export const Desc = styled.div` color: ${theme('baseColor.red')}; margin-left: 10px; + width: 82%; + max-width: 82%; ` diff --git a/src/containers/tool/ErrorBox/styles/header.ts b/src/containers/tool/ErrorBox/styles/header.ts index 52af2975f..906425d74 100755 --- a/src/containers/tool/ErrorBox/styles/header.ts +++ b/src/containers/tool/ErrorBox/styles/header.ts @@ -5,7 +5,6 @@ import css, { theme, animate } from '@/css' export const Wrapper = styled.div` ${css.row()}; - padding: 15px 22px; ` export const ErrorIcon = styled(Img)` fill: ${theme('baseColor.red')}; @@ -23,5 +22,4 @@ export const Title = styled.div` // color: ${theme('article.digest')}; export const Desc = styled.div` color: ${theme('baseColor.red')}; - opacity: 0.6; ` diff --git a/src/containers/tool/ErrorBox/styles/index.ts b/src/containers/tool/ErrorBox/styles/index.ts index 41003c78f..cd013f003 100755 --- a/src/containers/tool/ErrorBox/styles/index.ts +++ b/src/containers/tool/ErrorBox/styles/index.ts @@ -8,10 +8,10 @@ export const Wrapper = styled.div` position: fixed; left: calc(50% - 245px); bottom: 30px; - ${css.row('align-center')}; + ${css.row('align-start')}; border: 1px solid; border-color: ${theme('baseColor.red')}; - height: 40px; + height: auto; width: 520px; border-radius: 12px; box-shadow: rgba(17, 17, 26, 0.1) 0px 1px 0px, rgba(17, 17, 26, 0.1) 0px 8px 24px, @@ -19,15 +19,19 @@ export const Wrapper = styled.div` background-color: #ffefee; z-index: 10; - padding: 0 15px; + padding: 8px 15px; animation: ${animate.jump} 0.3s linear; ` +export const IconWrapper = styled.div` + ${css.row('align-both')}; +` export const WarningIcon = styled(WarningSVG)` ${css.size(12)}; - fill: tomato; + fill: ${theme('baseColor.red')}; margin-right: 8px; margin-bottom: 1px; + margin-top: 4px; ` export const ResetButton = styled.div` ${css.row('align-both')}; @@ -46,13 +50,19 @@ export const ResetButton = styled.div` cursor: pointer; transition: all 0.2s; ` +export const ButtonGroup = styled.div` + ${css.row('align-center', 'justify-end')}; + width: 120px; + min-width: 120px; +` export const LearnMoreButton = styled.div` ${css.row('align-both')}; - width: 60px; height: 18px; border-radius: 6px; font-size: 11px; + padding: 2px 6px; background: tomato; + font-weight: 500; margin-right: 8px; ` export const MoreLink = styled(Link)` diff --git a/src/containers/unit/Comments/schema.ts b/src/containers/unit/Comments/schema.ts index 808408956..be1838096 100755 --- a/src/containers/unit/Comments/schema.ts +++ b/src/containers/unit/Comments/schema.ts @@ -155,7 +155,7 @@ const pagedPublishedComments = gql` query pagedPublishedComments( $login: String! $thread: Thread, - $filter: PagedFilter! + $filter: PagiFilter! ) { pagedPublishedComments(login: $login, thread: $thread, filter: $filter) { entries { diff --git a/src/containers/unit/ModeLine/CommunityLayout/index.tsx b/src/containers/unit/ModeLine/CommunityLayout/index.tsx index 64f88a19b..2f7209f00 100644 --- a/src/containers/unit/ModeLine/CommunityLayout/index.tsx +++ b/src/containers/unit/ModeLine/CommunityLayout/index.tsx @@ -5,6 +5,7 @@ import type { TArticle, TTag, TGroupedTags } from '@/spec' import useViewingCommunity from '@/hooks/useViewingCommunity' import { scrollToHeader } from '@/dom' +import { assetSrc } from '@/helper' import MobileThreadNavi from '@/widgets/MobileThreadNavi' import ArticlesFilter from '@/widgets/ArticlesFilter' @@ -45,7 +46,7 @@ const CommunityLayout: FC = ({ - + = ({ community }) => { return ( - + {community.title} {community.desc} diff --git a/src/containers/user/UserBilling/schema.ts b/src/containers/user/UserBilling/schema.ts index 177cfeff2..72c37791b 100755 --- a/src/containers/user/UserBilling/schema.ts +++ b/src/containers/user/UserBilling/schema.ts @@ -8,7 +8,7 @@ const simpleMutation = gql` } ` const pagedBillRecords = gql` - query ($filter: PagedFilter!) { + query ($filter: PagiFilter!) { pagedBillRecords(filter: $filter) { entries { id diff --git a/src/containers/user/UserLister/schema.ts b/src/containers/user/UserLister/schema.ts index 13f2bba04..4e093ed35 100755 --- a/src/containers/user/UserLister/schema.ts +++ b/src/containers/user/UserLister/schema.ts @@ -14,7 +14,7 @@ const pagedUsers = gql` ` const pagedCommunitySubscribers = gql` - query($id: ID, $community: String, $filter: PagedFilter!, $userHasLogin: Boolean!) { + query($id: ID, $community: String, $filter: PagiFilter!, $userHasLogin: Boolean!) { pagedCommunitySubscribers(id: $id, community: $community, filter: $filter) { entries { ${F.author} @@ -27,7 +27,7 @@ const pagedCommunitySubscribers = gql` ` const pagedFollowers = gql` - query($userId: ID, $filter: PagedFilter!, $userHasLogin: Boolean!) { + query($userId: ID, $filter: PagiFilter!, $userHasLogin: Boolean!) { pagedFollowers(userId: $userId, filter: $filter) { entries { ${F.author} @@ -40,7 +40,7 @@ const pagedFollowers = gql` ` const pagedFollowings = gql` - query($userId: ID, $filter: PagedFilter!, $userHasLogin: Boolean!) { + query($userId: ID, $filter: PagiFilter!, $userHasLogin: Boolean!) { pagedFollowings(userId: $userId, filter: $filter) { entries { ${F.author} @@ -52,7 +52,7 @@ const pagedFollowings = gql` } ` const pagedCommunityEditors = gql` - query($id: ID!, $filter: PagedFilter!, $userHasLogin: Boolean!) { + query($id: ID!, $filter: PagiFilter!, $userHasLogin: Boolean!) { pagedCommunityEditors(id: $id, filter: $filter) { entries { ${F.author} diff --git a/src/schemas/fragments/base.ts b/src/schemas/fragments/base.ts index 7e1f9021d..f56f9b58b 100755 --- a/src/schemas/fragments/base.ts +++ b/src/schemas/fragments/base.ts @@ -5,7 +5,6 @@ import EMOTION from '@/constant/emotion' import { titleCase } from '@/fmt' export const community = ` - id title slug index diff --git a/src/schemas/pages/community.ts b/src/schemas/pages/community.ts index c8ca0ef60..0914f3f14 100755 --- a/src/schemas/pages/community.ts +++ b/src/schemas/pages/community.ts @@ -2,7 +2,7 @@ import F from '../fragments' // contributesDigest export const subscribedCommunities = ` - query subscribedCommunities($login: String, $filter: PagedFilter!) { + query subscribedCommunities($login: String, $filter: PagiFilter!) { subscribedCommunities(login: $login, filter: $filter) { entries { ${F.community} @@ -12,8 +12,9 @@ export const subscribedCommunities = ` } } ` + export const community = ` - query community($slug: String, $userHasLogin: Boolean!, $incViews: Boolean) { + query community($slug: String!, $userHasLogin: Boolean!, $incViews: Boolean) { community(slug: $slug, incViews: $incViews) { ${F.community} viewerHasSubscribed @include(if: $userHasLogin) diff --git a/src/schemas/pages/misc.ts b/src/schemas/pages/misc.ts index 8dc7a18cc..d7a0ddfe8 100755 --- a/src/schemas/pages/misc.ts +++ b/src/schemas/pages/misc.ts @@ -11,7 +11,7 @@ export const pagedArticleTags = ` ` export const pagedCategories = ` - query($filter: PagedFilter!) { + query($filter: PagiFilter!) { pagedCategories(filter: $filter) { entries { id diff --git a/src/schemas/pages/post.ts b/src/schemas/pages/post.ts index 627a7fd89..bebd66241 100755 --- a/src/schemas/pages/post.ts +++ b/src/schemas/pages/post.ts @@ -9,7 +9,7 @@ export const post = ` } ` export const pagedPosts = ` - query($filter: PagedPostsFilter, $userHasLogin: Boolean!) { + query($filter: PagedPostsFilter!, $userHasLogin: Boolean!) { pagedPosts(filter: $filter) { entries { ${F.article} @@ -22,7 +22,6 @@ export const pagedPosts = ` } } digest - linkAddr commentsParticipants { ${F.author} } @@ -35,7 +34,7 @@ export const pagedPosts = ` ` export const pagedPublishedPosts = ` - query($login: String!, $filter: PagedFilter!, $userHasLogin: Boolean!) { + query($login: String!, $filter: PagiFilter!, $userHasLogin: Boolean!) { pagedPublishedPosts(login: $login, filter: $filter) { entries { ${F.article} diff --git a/src/spec/dashboard.d.ts b/src/spec/dashboard.d.ts index d8ceaba42..e830a1ebf 100644 --- a/src/spec/dashboard.d.ts +++ b/src/spec/dashboard.d.ts @@ -1,4 +1,5 @@ import type { TColorName } from './color' +import type { TMediaReport } from './community' export type TAvatarLayout = 'circle' | 'square' export type TBrandLayout = 'both' | 'logo' | 'text' @@ -17,6 +18,16 @@ export type TRSSType = 'digest' | 'full' export type TBroadcastLayout = 'default' | 'center' export type TBroadcastArticleLayout = 'default' | 'simple' +// TODO: to complete +export type TDashboard = { + title?: string + logo?: string + favicon?: string + bio?: string + homepage?: string + mediaReports?: TMediaReport[] +} + export type TBroadcastConfig = { // banner broadcastLayout: TBroadcastLayout diff --git a/src/spec/index.ts b/src/spec/index.ts index e731aa14b..166e58707 100755 --- a/src/spec/index.ts +++ b/src/spec/index.ts @@ -89,11 +89,13 @@ export type { TLinkItem, TGroupedLinks, TChangeMode, + TUploadPreview, } from './utils' export type { TGQLError } from './graphql' export type { + TDashboard, TAvatarLayout, TTopbarLayout, TBrandLayout, diff --git a/src/spec/utils.d.ts b/src/spec/utils.d.ts index 78a6f9749..e2790f3e8 100755 --- a/src/spec/utils.d.ts +++ b/src/spec/utils.d.ts @@ -218,3 +218,9 @@ export type TGroupedLinks = { } export type TChangeMode = 'create' | 'update' + +export type TUploadPreview = { + height: number + width: number + radius: number +} diff --git a/src/stores/ViewingStore/index.ts b/src/stores/ViewingStore/index.ts index d626ebf89..1b27f68e7 100755 --- a/src/stores/ViewingStore/index.ts +++ b/src/stores/ViewingStore/index.ts @@ -5,11 +5,19 @@ import { values, merge, includes } from 'ramda' -import type { TRootStore, TUser, TArticle, TArticleMeta, TThread, TAccount } from '@/spec' +import type { + TRootStore, + TUser, + TArticle, + TArticleMeta, + TThread, + TAccount, + TCommunity, +} from '@/spec' import { ARTICLE_THREAD, THREAD } from '@/constant/thread' -import { T, getParent, Instance, markStates } from '@/mobx' +import { T, getParent, Instance, markStates, toJS } from '@/mobx' import { viewingChanged } from '@/signal' import { User, Community, Post, Changelog } from '@/model' @@ -56,6 +64,9 @@ const ViewingStore = T.model('ViewingStore', { mark(sobj) viewingChanged(viewingArticle) }, + updateViewingCommunity(args: TCommunity): void { + self.community = { ...toJS(self.community), ...args } + }, changeCommunity(slug): void { self.community.slug = slug }, diff --git a/src/widgets/CommunityBadgeSelector/index.tsx b/src/widgets/CommunityBadgeSelector/index.tsx index 3cd67e4a8..db8420f7f 100755 --- a/src/widgets/CommunityBadgeSelector/index.tsx +++ b/src/widgets/CommunityBadgeSelector/index.tsx @@ -5,6 +5,7 @@ import { HCN } from '@/constant/name' import { cutRest } from '@/fmt' import { selectCommunity } from '@/signal' +import { assetSrc } from '@/helper' import Tooltip from '@/widgets/Tooltip' import CommunityCard from '@/widgets/Cards/CommunityCard' @@ -29,7 +30,7 @@ const CommunityBadgeSelector: FC = ({ community, mode = 'publish' }) => 所属社区: )} - <Logo src={community.logo} slug={community.slug} /> + <Logo src={assetSrc(community.logo)} slug={community.slug} /> <Tooltip content={<CommunityCard item={community} />} delay={500} placement="bottom"> <div>{cutRest(community.title || '--', 15)}</div> </Tooltip> diff --git a/src/widgets/CommunityDigest/HeaderLayout/CommunityBrief.tsx b/src/widgets/CommunityDigest/HeaderLayout/CommunityBrief.tsx index 5d2cdd55b..216bb5fd9 100644 --- a/src/widgets/CommunityDigest/HeaderLayout/CommunityBrief.tsx +++ b/src/widgets/CommunityDigest/HeaderLayout/CommunityBrief.tsx @@ -1,6 +1,7 @@ import { FC } from 'react' import { observer } from 'mobx-react' +import { assetSrc } from '@/helper' import useViewingCommunity from '@/hooks/useViewingCommunity' // import CommunityJoinSign from '@/widgets/CommunityJoinSign' @@ -15,16 +16,12 @@ import { } from '../styles/header_layout/community_brief' // import { subscribeCommunity, unsubscribeCommunity } from '../logic' -type TProps = { - // -} - -const CommunityBrief: FC<TProps> = () => { +const CommunityBrief: FC = () => { const community = useViewingCommunity() return ( <Wrapper> - <Logo src={community.logo} noLazy /> + <Logo src={assetSrc(community.logo)} noLazy /> <CommunityInfo> <TitleWrapper> <Title> diff --git a/src/widgets/CommunityList/index.tsx b/src/widgets/CommunityList/index.tsx index 4fbea5794..98f01f45d 100755 --- a/src/widgets/CommunityList/index.tsx +++ b/src/widgets/CommunityList/index.tsx @@ -9,6 +9,8 @@ import { isEmpty } from 'ramda' import type { TCommunity } from '@/spec' import { buildLog } from '@/logger' +import { assetSrc } from '@/helper' + import Tooltip from '@/widgets/Tooltip' import CommunityCard from '@/widgets/Cards/CommunityCard' @@ -45,7 +47,7 @@ const CommunityList: FC<TProps> = ({ {items.map((community) => ( <Tooltip key={community.id} placement="bottom" content={<CommunityCard item={community} />}> <Linker href={`/${community.slug}`} bottom={bottom} right={right}> - <Logo src={community.logo} size={size} slug={community.slug} /> + <Logo src={assetSrc(community.logo)} size={size} slug={community.slug} /> </Linker> </Tooltip> ))} diff --git a/src/widgets/Input/styles/textarea.ts b/src/widgets/Input/styles/textarea.ts index cca971ee4..072f67c20 100755 --- a/src/widgets/Input/styles/textarea.ts +++ b/src/widgets/Input/styles/textarea.ts @@ -16,7 +16,7 @@ export const Wrapper = styled(TextareaAutosize).attrs<TTestable>(({ testid }) => opacity: 0.9; min-height: 56px; padding: 10px; - background-color: white; + background-color: ${theme('alphaBg')}; border: 1px solid; resize: none; overflow: hidden; diff --git a/src/widgets/Navigator/BriefView.tsx b/src/widgets/Navigator/BriefView.tsx index ff47fd90d..78ecf16ff 100755 --- a/src/widgets/Navigator/BriefView.tsx +++ b/src/widgets/Navigator/BriefView.tsx @@ -2,6 +2,8 @@ import { FC, memo } from 'react' import type { TCommunity } from '@/spec' import { ICON_CMD } from '@/config' +import { assetSrc } from '@/helper' + import Tooltip from '@/widgets/Tooltip' import { @@ -31,7 +33,7 @@ const BriefView: FC<TProps> = ({ community }) => { <CardWrapper> <CommunityWrapper> {community.logo ? ( - <CommunityLogo src={community.logo} slug={community.slug} /> + <CommunityLogo src={assetSrc(community.logo)} slug={community.slug} /> ) : ( <LogoHolder src={CommunityLogoHolder} /> )} diff --git a/src/widgets/Navigator/CardPopover.tsx b/src/widgets/Navigator/CardPopover.tsx index dc312b430..c689f27ee 100755 --- a/src/widgets/Navigator/CardPopover.tsx +++ b/src/widgets/Navigator/CardPopover.tsx @@ -3,6 +3,8 @@ import { FC, memo } from 'react' import type { TCommunity } from '@/spec' import CommunityStatesPad from '@/widgets/CommunityStatesPad' +import { assetSrc } from '@/helper' + import { Wrapper, Body, @@ -21,7 +23,7 @@ type TProps = { const CardPopover: FC<TProps> = ({ community }) => ( <Wrapper> <Body> - <CommunityLogo src={community.logo} slug={community.slug} /> + <CommunityLogo src={assetSrc(community.logo)} slug={community.slug} /> <CommunityInfo> <Title>{community.title} {community.desc} diff --git a/src/widgets/OSSUploader/PreviewBlock.tsx b/src/widgets/OSSUploader/PreviewBlock.tsx new file mode 100644 index 000000000..902d3a7be --- /dev/null +++ b/src/widgets/OSSUploader/PreviewBlock.tsx @@ -0,0 +1,20 @@ +import { FC } from 'react' + +import { assetSrc } from '@/helper' + +import type { TUploadPreview } from '@/spec' +import { Wrapper, PreviewImg } from './styles/preview_block' + +type TProps = { + url: string +} & TUploadPreview + +const PreviewBlock: FC = ({ url, height, width, radius }) => { + return ( + + + + ) +} + +export default PreviewBlock diff --git a/src/widgets/OSSUploader/constant.ts b/src/widgets/OSSUploader/constant.ts new file mode 100644 index 000000000..a3e506523 --- /dev/null +++ b/src/widgets/OSSUploader/constant.ts @@ -0,0 +1,11 @@ +export const STS = { + AK: 'sts.ak', + AS: 'sts.as', + TOKEN: 'sts.token', + EXPIRATION: 'sts.expiration', +} + +export const OSS_CONFIG = { + REGION: 'oss-cn-shanghai', + BUCKET: 'groupher', +} diff --git a/src/widgets/OSSUploader/helper.ts b/src/widgets/OSSUploader/helper.ts index 6e224b890..688d7e9c5 100755 --- a/src/widgets/OSSUploader/helper.ts +++ b/src/widgets/OSSUploader/helper.ts @@ -1,24 +1,83 @@ import { startsWith } from 'ramda' + +import { toast } from '@/signal' +import { buildGQClient } from '@/utils/graphql' +import uid from '@/utils/uid' +import BStore from '@/utils/bstore' + import { ASSETS_ENDPOINT } from '@/config' +import type { TTokens } from './spec' +import { STS, OSS_CONFIG } from './constant' +import S from './schema' + +// const sessionState = gqClient.request(P.sessionState) +const gqClient = buildGQClient() + +export const applyUploadTokensIfNeed = async (): Promise => { + if (BStore.get(STS.TOKEN) && !isTokenExpired()) return + + const tokens = await applyStsTokens() + persistTokens(tokens) +} + +// see: https://sentry.io/answers/how-to-compare-two-dates-with-javascript/ +const isTokenExpired = () => { + const expirationDate = new Date(BStore.get(STS.EXPIRATION)) + const now = new Date() + + return expirationDate < now +} + +const applyStsTokens = async (): Promise => { + const { applyUploadTokens } = await gqClient.request(S.applyUploadTokens) + + return { ...applyUploadTokens } +} + +const persistTokens = (tokns: TTokens): void => { + const { accessKeyId, securityToken, accessKeySecret, expiration } = tokns + + BStore.set(STS.TOKEN, securityToken) + BStore.set(STS.AK, accessKeyId) + BStore.set(STS.AS, accessKeySecret) + BStore.set(STS.EXPIRATION, expiration) +} + const getOSSDir = (): string => { const curDateTime = new Date() const year = curDateTime.getFullYear() const month = curDateTime.getMonth() + 1 const day = curDateTime.getDate() - return `ugc/${year}-${month}-${day}` + return `ugc/_tmp/${year}-${month}-${day}` } export const initOSSClient = (): any => { + applyUploadTokensIfNeed() + // @ts-ignore const ossClient = new OSS({ - region: process.env.NEXT_PUBLIC_ALI_OSS_RESION, - accessKeyId: process.env.NEXT_PUBLIC_ALI_ACCESS_KEY, - accessKeySecret: process.env.NEXT_PUBLIC_ALI_ACCESS_SECRET, - bucket: process.env.NEXT_PUBLIC_ALI_OSS_BUCKET, - /* internal: true, */ - /* secure: true, */ + /** + * 从STS服务获取的安全令牌(SecurityToken), 对应的 policy 在阿里控制台上设置 + */ + accessKeyId: BStore.get(STS.AK), + accessKeySecret: BStore.get(STS.AS), + stsToken: BStore.get(STS.TOKEN), + region: OSS_CONFIG.REGION, + bucket: OSS_CONFIG.BUCKET, + refreshSTSToken: async () => { + const tokens = await applyStsTokens() + const { accessKeyId, securityToken, accessKeySecret } = tokens + persistTokens(tokens) + + return { + accessKeyId, + accessKeySecret, + stsToken: securityToken, + } + }, + refreshSTSTokenInterval: 300000, }) return ossClient @@ -36,14 +95,11 @@ export const doUploadFile = (ossClient, file, filePrefix, callbacks): void => { const fileSize = file.size / 1024 / 1024 // eslint-disable-next-line no-alert - if (fileSize > 1) return alert('不支持大于 1MB 的文件') + if (fileSize > 2) return alert('不支持大于 2MB 的文件') callbacks.onStart() - const timestamp = new Date().getTime() - // onUploadStart() - - const filename = `${timestamp}_${file.name}` + const filename = `${uid.gen()}_${file.name}` const fileFullname = filePrefix ? `${filePrefix}_${filename}` : filename const OSSDir = getOSSDir() @@ -57,7 +113,10 @@ export const doUploadFile = (ossClient, file, filePrefix, callbacks): void => { }) .catch((err) => { callbacks.onError('上传失败') - // onUploadError(err) - console.error(err) + toast('文件上传失败') + // BStore.remove(STS.AK) + // BStore.remove(STS.AS) + // BStore.remove(STS.TOKEN) + // BStore.remove(STS.EXPIRATION) }) } diff --git a/src/widgets/OSSUploader/index.tsx b/src/widgets/OSSUploader/index.tsx index 58b739a29..26392eb19 100755 --- a/src/widgets/OSSUploader/index.tsx +++ b/src/widgets/OSSUploader/index.tsx @@ -8,9 +8,20 @@ import { FC, memo, ReactNode, useState, useEffect, useRef, useCallback } from 'r import { buildLog } from '@/logger' import uid from '@/utils/uid' - -import { Wrapper, InnerBorder, Label, HintIcon, TurboIcon, InputFile } from './styles' -import { initOSSClient, handleUploadFile } from './helper' +import { assetPath } from '@/helper' + +import { + Wrapper, + InnerWrapper, + Label, + HintIcon, + TurboIcon, + InputFile, + CloseBtn, + CrossIcon, +} from './styles' +import PreviewBlock from './PreviewBlock' +import { initOSSClient, handleUploadFile, applyUploadTokensIfNeed } from './helper' /* eslint-disable-next-line */ const log = buildLog('w:OSSUploader:index') @@ -18,8 +29,13 @@ const log = buildLog('w:OSSUploader:index') type TProps = { children: ReactNode onUploadDone?: (url: string) => void + onDelete?: () => void filePrefix?: string | null fileType?: string + previewUrl?: string + previewHeight?: number + previewWidth?: number + previewRadius?: number } const OSSUploader: FC = ({ @@ -27,6 +43,11 @@ const OSSUploader: FC = ({ fileType = 'image/*', filePrefix = null, onUploadDone = log, + onDelete = log, + previewUrl = '', // 'https://static.groupher.com/ugc/_tmp/2023-10-13/Linth.png', + previewHeight = 50, + previewWidth = 50, + previewRadius = 4, }) => { const [loaded, setOnLoad] = useState(false) const [uniqueId, setUniqueId] = useState(null) @@ -37,10 +58,17 @@ const OSSUploader: FC = ({ useEffect(() => { if (loaded) { - const ossClient = initOSSClient() + // see https://stackoverflow.com/a/53572588 + // eslint-disable-next-line no-inner-declarations + async function initOSS() { + await applyUploadTokensIfNeed() + const ossClient = initOSSClient() + + setOSSClient(ossClient) + setUniqueId(uid.gen()) + } - setOSSClient(ossClient) - setUniqueId(uid.gen()) + initOSS() } }, [loaded]) @@ -51,39 +79,59 @@ const OSSUploader: FC = ({ const onDone = useCallback( (url) => { setLoading(false) - onUploadDone(url) + // console.log('## url: ', url) + // console.log('## asset url: ', assetPath(url)) + onUploadDone(assetPath(url)) }, [onUploadDone], ) const onError = useCallback((msg) => { - alert(msg) setLoading(false) }, []) const callbacks = { onStart, onDone, onError } + const showPreview = !!previewUrl return (