From 9c3acfc1cc6e9b458cf132d7b09dbec79f8d504a Mon Sep 17 00:00:00 2001 From: Kechicode <186776112+Kechicode@users.noreply.github.com> Date: Fri, 6 Dec 2024 12:03:12 +0800 Subject: [PATCH 01/16] feat(DraftDetail): enhance campaign selection UI and logic --- lang/default.json | 12 +- lang/en.json | 12 +- lang/zh-Hans.json | 14 +- lang/zh-Hant.json | 14 +- .../BottomBar/MobileSettingsDialog/index.tsx | 8 +- src/components/Editor/BottomBar/index.tsx | 6 +- .../Editor/SelectCampaign/index.tsx | 173 ++++++++++++------ .../Editor/SettingsDialog/List/index.tsx | 37 +++- .../Editor/SettingsDialog/index.tsx | 6 +- .../Editor/Sidebar/Campaign/index.tsx | 2 +- src/views/Me/DraftDetail/BottomBar.tsx | 15 +- .../Me/DraftDetail/SettingsButton/index.tsx | 15 +- src/views/Me/DraftDetail/Sidebar/index.tsx | 15 +- src/views/Me/DraftDetail/gql.ts | 2 +- 14 files changed, 231 insertions(+), 100 deletions(-) diff --git a/lang/default.json b/lang/default.json index 784c113eba..ec07a834b3 100644 --- a/lang/default.json +++ b/lang/default.json @@ -944,6 +944,9 @@ "defaultMessage": "Verification successful", "description": "src/components/GlobalToast/index.tsx" }, + "DpbBcd": { + "defaultMessage": "Select Activity..." + }, "DqQvtL": { "defaultMessage": "Unblock", "description": "src/views/Me/Settings/Blocked/ToggleBlockButton.tsx" @@ -1565,6 +1568,9 @@ "defaultMessage": "guide", "description": "src/components/Forms/PaymentForm/PayTo/SetAmount/SetAmountHeader/WhyOptimismDialog/index.tsx" }, + "P/7t1k": { + "defaultMessage": "Please select a date of activity" + }, "P3y9Bo": { "defaultMessage": "Go to sign", "description": "src/components/Forms/PaymentForm/BindWallet/index.tsx" @@ -1901,9 +1907,6 @@ "defaultMessage": "More", "description": "src/views/ArticleDetail/AuthorSidebar/Tabs/index.tsx" }, - "VrK0Q0": { - "defaultMessage": "Please select..." - }, "VrOoVf": { "defaultMessage": "Matters will never ask your wallet key through any channel.", "description": "src/components/Forms/WalletAuthForm/Select.tsx" @@ -2307,6 +2310,9 @@ "d5+b8r": { "defaultMessage": "Restricted content" }, + "d5bM8A": { + "defaultMessage": "Select Date..." + }, "dAPUJp": { "defaultMessage": "The dazzling light of a meteor shower is enough to illuminate the night sky. The Meteor Canoe badge signifies your participation in the Nomad Matters.", "description": "src/views/User/UserProfile/BadgeNomadLabel/index.tsx" diff --git a/lang/en.json b/lang/en.json index 2fcbe0300c..3db4d8b52d 100644 --- a/lang/en.json +++ b/lang/en.json @@ -944,6 +944,9 @@ "defaultMessage": "Verification successful", "description": "src/components/GlobalToast/index.tsx" }, + "DpbBcd": { + "defaultMessage": "Select Activity..." + }, "DqQvtL": { "defaultMessage": "Unblock", "description": "src/views/Me/Settings/Blocked/ToggleBlockButton.tsx" @@ -1565,6 +1568,9 @@ "defaultMessage": "guide", "description": "src/components/Forms/PaymentForm/PayTo/SetAmount/SetAmountHeader/WhyOptimismDialog/index.tsx" }, + "P/7t1k": { + "defaultMessage": "Please select a date of activity" + }, "P3y9Bo": { "defaultMessage": "Go to sign", "description": "src/components/Forms/PaymentForm/BindWallet/index.tsx" @@ -1901,9 +1907,6 @@ "defaultMessage": "More", "description": "src/views/ArticleDetail/AuthorSidebar/Tabs/index.tsx" }, - "VrK0Q0": { - "defaultMessage": "Please select..." - }, "VrOoVf": { "defaultMessage": "Matters will never ask your wallet key through any channel.", "description": "src/components/Forms/WalletAuthForm/Select.tsx" @@ -2307,6 +2310,9 @@ "d5+b8r": { "defaultMessage": "Restricted content" }, + "d5bM8A": { + "defaultMessage": "Select Date..." + }, "dAPUJp": { "defaultMessage": "The dazzling light of a meteor shower is enough to illuminate the night sky. The Meteor Canoe badge signifies your participation in the Nomad Matters.", "description": "src/views/User/UserProfile/BadgeNomadLabel/index.tsx" diff --git a/lang/zh-Hans.json b/lang/zh-Hans.json index 82a99d37af..377a667420 100644 --- a/lang/zh-Hans.json +++ b/lang/zh-Hans.json @@ -529,7 +529,7 @@ "defaultMessage": "上传档案" }, "6pc948": { - "defaultMessage": "投稿七日书自由写" + "defaultMessage": "参与活动" }, "6q0G5e": { "defaultMessage": "加入成功", @@ -944,6 +944,9 @@ "defaultMessage": "验证成功", "description": "src/components/GlobalToast/index.tsx" }, + "DpbBcd": { + "defaultMessage": "选择活动..." + }, "DqQvtL": { "defaultMessage": "解除屏蔽", "description": "src/views/Me/Settings/Blocked/ToggleBlockButton.tsx" @@ -1565,6 +1568,9 @@ "defaultMessage": "教学指南", "description": "src/components/Forms/PaymentForm/PayTo/SetAmount/SetAmountHeader/WhyOptimismDialog/index.tsx" }, + "P/7t1k": { + "defaultMessage": "请选定参与活动的投稿日程" + }, "P3y9Bo": { "defaultMessage": "前往签署", "description": "src/components/Forms/PaymentForm/BindWallet/index.tsx" @@ -1901,9 +1907,6 @@ "defaultMessage": "相关推荐", "description": "src/views/ArticleDetail/AuthorSidebar/Tabs/index.tsx" }, - "VrK0Q0": { - "defaultMessage": "请选择⋯" - }, "VrOoVf": { "defaultMessage": "Matters 不会透过任何渠道主动询问你的钱包私钥。", "description": "src/components/Forms/WalletAuthForm/Select.tsx" @@ -2307,6 +2310,9 @@ "d5+b8r": { "defaultMessage": "限制级内容" }, + "d5bM8A": { + "defaultMessage": "投稿日程⋯" + }, "dAPUJp": { "defaultMessage": "流星雨的绚烂光芒足以点亮夜空。流星号徽章纪念你曾参与「游牧者计划」。", "description": "src/views/User/UserProfile/BadgeNomadLabel/index.tsx" diff --git a/lang/zh-Hant.json b/lang/zh-Hant.json index 4d31476398..d08e228708 100644 --- a/lang/zh-Hant.json +++ b/lang/zh-Hant.json @@ -529,7 +529,7 @@ "defaultMessage": "上傳檔案" }, "6pc948": { - "defaultMessage": "投稿七日書自由寫" + "defaultMessage": "參與活動" }, "6q0G5e": { "defaultMessage": "加入成功", @@ -944,6 +944,9 @@ "defaultMessage": "驗證成功", "description": "src/components/GlobalToast/index.tsx" }, + "DpbBcd": { + "defaultMessage": "選擇活動⋯" + }, "DqQvtL": { "defaultMessage": "解除封鎖", "description": "src/views/Me/Settings/Blocked/ToggleBlockButton.tsx" @@ -1565,6 +1568,9 @@ "defaultMessage": "教學指南", "description": "src/components/Forms/PaymentForm/PayTo/SetAmount/SetAmountHeader/WhyOptimismDialog/index.tsx" }, + "P/7t1k": { + "defaultMessage": "請選定參與活動的投稿日程" + }, "P3y9Bo": { "defaultMessage": "前往簽署", "description": "src/components/Forms/PaymentForm/BindWallet/index.tsx" @@ -1901,9 +1907,6 @@ "defaultMessage": "相關推薦", "description": "src/views/ArticleDetail/AuthorSidebar/Tabs/index.tsx" }, - "VrK0Q0": { - "defaultMessage": "請選擇⋯" - }, "VrOoVf": { "defaultMessage": "Matters 不會透過任何渠道主動詢問你的錢包私鑰。", "description": "src/components/Forms/WalletAuthForm/Select.tsx" @@ -2307,6 +2310,9 @@ "d5+b8r": { "defaultMessage": "限制級內容" }, + "d5bM8A": { + "defaultMessage": "投稿日程⋯" + }, "dAPUJp": { "defaultMessage": "流星雨的絢爛光芒足以點亮夜空。流星號徽章紀念你曾參與「遊牧者計畫」。", "description": "src/views/User/UserProfile/BadgeNomadLabel/index.tsx" diff --git a/src/components/Editor/BottomBar/MobileSettingsDialog/index.tsx b/src/components/Editor/BottomBar/MobileSettingsDialog/index.tsx index 7fc405d49e..5b9b9918d1 100644 --- a/src/components/Editor/BottomBar/MobileSettingsDialog/index.tsx +++ b/src/components/Editor/BottomBar/MobileSettingsDialog/index.tsx @@ -20,7 +20,8 @@ const BaseMobileSettingsDialog = ({ children, canComment, toggleComment, - appliedCampaign, + campaigns, + selectedCampaign, selectedStage, editCampaign, indented, @@ -62,7 +63,7 @@ const BaseMobileSettingsDialog = ({ {/* campaign */} - {appliedCampaign && editCampaign && ( + {campaigns && selectedCampaign && editCampaign && (

diff --git a/src/components/Editor/BottomBar/index.tsx b/src/components/Editor/BottomBar/index.tsx index 2d1e24f0f5..7f3b7ec1bb 100644 --- a/src/components/Editor/BottomBar/index.tsx +++ b/src/components/Editor/BottomBar/index.tsx @@ -92,7 +92,8 @@ const BottomBar: React.FC = ({ canComment, toggleComment, - appliedCampaign, + campaigns, + selectedCampaign, selectedStage, editCampaign, @@ -148,7 +149,8 @@ const BottomBar: React.FC = ({ toggleComment, disableChangeCanComment: article?.canComment, - appliedCampaign, + campaigns, + selectedCampaign, selectedStage, editCampaign, diff --git a/src/components/Editor/SelectCampaign/index.tsx b/src/components/Editor/SelectCampaign/index.tsx index 32f8ada3fb..e5cd71d680 100644 --- a/src/components/Editor/SelectCampaign/index.tsx +++ b/src/components/Editor/SelectCampaign/index.tsx @@ -1,9 +1,9 @@ import gql from 'graphql-tag' -import { useContext } from 'react' +import { useContext, useEffect, useState } from 'react' import { FormattedMessage } from 'react-intl' import { datetimeFormat } from '~/common/utils' -import { Form, LanguageContext } from '~/components' +import { Form, LanguageContext, Spacer } from '~/components' import { ArticleCampaignInput, CampaignState, @@ -11,91 +11,151 @@ import { } from '~/gql/graphql' export interface SelectCampaignProps { - appliedCampaign: EditorSelectCampaignFragment - selectedStage?: string + campaigns: EditorSelectCampaignFragment[] + selectedCampaign: EditorSelectCampaignFragment | undefined + selectedStage: string | undefined editCampaign: (value?: ArticleCampaignInput) => any } -export const getSelectCampaign = ({ +export const getSelectCampaigns = ({ applied, attached, createdAt, }: { - applied?: EditorSelectCampaignFragment + applied?: EditorSelectCampaignFragment[] attached: Array<{ campaign: { id: string } stage?: { id: string } | null }> - createdAt: string // draft or article creation time + createdAt: string }) => { - const { start } = applied?.writingPeriod || {} - const isCampaignStarted = !!start && new Date(createdAt) >= new Date(start) - const isCampaignActive = applied?.state === CampaignState.Active + const campaigns = applied?.filter((campaign) => { + const { start, end } = campaign?.writingPeriod || {} + const isCampaignStarted = !!start && new Date(createdAt) >= new Date(start) + const isCampaignEnded = !!end && new Date(createdAt) >= new Date(end) + const isCampaignActive = campaign?.state === CampaignState.Active - // only show appliedCampaign if the article or draft is created during the writing period - const appliedCampaign = - isCampaignStarted && isCampaignActive ? applied : undefined - const selectedCampaign = attached.filter( - (c) => c.campaign.id === applied?.id - )[0] - const selectedStage = selectedCampaign?.stage?.id + // only show appliedCampaign if the article or draft is created during the writing period + return isCampaignStarted && !isCampaignEnded && isCampaignActive + }) + + const selectedCampaign = campaigns?.find((campaign) => { + return attached.find((a) => a.campaign.id === campaign.id) + }) + + const selectedStage = attached.find( + (a) => a.campaign.id === selectedCampaign?.id + )?.stage?.id return { - appliedCampaign, + campaigns, + selectedCampaign, selectedStage, } } const SelectCampaign = ({ - appliedCampaign, + campaigns, + selectedCampaign, selectedStage, editCampaign, }: SelectCampaignProps) => { + const [selectedCampaignId, setSelectedCampaignId] = useState< + string | undefined + >(selectedCampaign?.id) + const [selectedStageId, setSelectedStageId] = useState( + selectedStage + ) + + useEffect(() => { + setSelectedCampaignId(selectedCampaign?.id) + setSelectedStageId(selectedStage) + }, [selectedCampaign, selectedStage]) + const { lang } = useContext(LanguageContext) - const RESET_OPTION = { - name: , + const RESET_CAMPAIGN_OPTION = { + name: , + value: undefined, + selected: !selectedCampaignId, + } + const RESET_STAGE_OPTION = { + name: , value: undefined, - selected: !selectedStage, + selected: !selectedStageId, } const now = new Date() - const availableStages = appliedCampaign.stages.filter((s) => { - const period = s.period + const availableStages = selectedCampaignId + ? campaigns + .find((c) => c.id === selectedCampaignId) + ?.stages.filter((s) => { + const period = s.period - if (!period) return false + if (!period) return false - return now >= new Date(period.start) - }) + return now >= new Date(period.start) + }) + : undefined return ( - - name="select-campaign" - onChange={(option) => - editCampaign( - option.value - ? { campaign: appliedCampaign.id, stage: option.value } - : undefined - ) - } - options={[ - RESET_OPTION, - ...availableStages.reverse().map((s) => { - return { - name: s.period?.start - ? `${s.name} - ${datetimeFormat.absolute({ - date: s.period.start, - lang, - optionalYear: false, - utc8: true, - })}` - : s.name, - value: s.id, - selected: s.id === selectedStage, - } - }), - ]} - size={14} - color="freeWriteBlue" - /> + <> + + name="select-campaign" + onChange={(option) => { + setSelectedCampaignId(option.value) + setSelectedStageId(undefined) + editCampaign( + option.value !== undefined ? { campaign: option.value } : undefined + ) + }} + options={[ + RESET_CAMPAIGN_OPTION, + ...campaigns.map((c) => { + return { + name: c.name, + value: c.id, + selected: c.id === selectedCampaignId, + } + }), + ]} + size={14} + color="freeWriteBlue" + /> + {selectedCampaignId && availableStages && availableStages.length > 0 && ( + <> + + + name="select-stage" + onChange={(option) => { + setSelectedStageId(option.value) + editCampaign( + option.value + ? { campaign: selectedCampaignId, stage: option.value } + : { campaign: selectedCampaignId } + ) + }} + options={[ + RESET_STAGE_OPTION, + ...availableStages.reverse().map((s) => { + return { + name: s.period?.start + ? `${s.name} - ${datetimeFormat.absolute({ + date: s.period.start, + lang, + optionalYear: false, + utc8: true, + })}` + : s.name, + value: s.id, + selected: s.id === selectedStageId, + } + }), + ]} + size={14} + color="freeWriteBlue" + /> + + )} + ) } @@ -103,6 +163,7 @@ SelectCampaign.fragments = gql` fragment EditorSelectCampaign on WritingChallenge { id state + name writingPeriod { start end diff --git a/src/components/Editor/SettingsDialog/List/index.tsx b/src/components/Editor/SettingsDialog/List/index.tsx index 4fad29f003..edfa838f22 100644 --- a/src/components/Editor/SettingsDialog/List/index.tsx +++ b/src/components/Editor/SettingsDialog/List/index.tsx @@ -1,6 +1,6 @@ import { FormattedMessage } from 'react-intl' -import { Dialog } from '~/components' +import { Dialog, toast } from '~/components' import { SetPublishISCNProps } from '~/components/Editor' import ListItem from '../../ListItem' @@ -53,7 +53,8 @@ const SettingsList = ({ collectionCount, tagsCount, - appliedCampaign, + campaigns, + selectedCampaign, selectedStage, editCampaign, @@ -68,6 +69,29 @@ const SettingsList = ({ toggleComment, disableChangeCanComment, } + const handleConfirm = () => { + if ( + selectedCampaign && + selectedCampaign.stages.length > 0 && + !selectedStage + ) { + toast.error({ + message: ( + + ), + }) + return + } + + if (onConfirm) { + onConfirm() + } else { + forward('confirm') + } + } return ( <> @@ -78,7 +102,7 @@ const SettingsList = ({ rightBtn={ forward('confirm')} + onClick={handleConfirm} loading={saving} disabled={disabled} /> @@ -108,7 +132,7 @@ const SettingsList = ({ )} - {appliedCampaign && editCampaign && ( + {campaigns && campaigns.length > 0 && editCampaign && (

@@ -187,7 +212,7 @@ const SettingsList = ({ /> forward('confirm')} + onClick={handleConfirm} loading={saving} disabled={disabled} /> diff --git a/src/components/Editor/SettingsDialog/index.tsx b/src/components/Editor/SettingsDialog/index.tsx index 5b3b8ae517..672d44542f 100644 --- a/src/components/Editor/SettingsDialog/index.tsx +++ b/src/components/Editor/SettingsDialog/index.tsx @@ -113,7 +113,8 @@ const BaseEditorSettingsDialog = ({ togglePublishISCN, iscnPublishSaving, - appliedCampaign, + campaigns, + selectedCampaign, selectedStage, editCampaign, @@ -183,7 +184,8 @@ const BaseEditorSettingsDialog = ({ } const campaignProps: Partial = { - appliedCampaign, + campaigns, + selectedCampaign, selectedStage, editCampaign, } diff --git a/src/components/Editor/Sidebar/Campaign/index.tsx b/src/components/Editor/Sidebar/Campaign/index.tsx index 743d94d197..011354311d 100644 --- a/src/components/Editor/Sidebar/Campaign/index.tsx +++ b/src/components/Editor/Sidebar/Campaign/index.tsx @@ -8,7 +8,7 @@ import Box from '../Box' import styles from './styles.module.css' const SidebarCampaign: React.FC> = (props) => { - if (!props.appliedCampaign || !props.editCampaign) { + if (!props.campaigns || props.campaigns.length === 0 || !props.editCampaign) { return null } diff --git a/src/views/Me/DraftDetail/BottomBar.tsx b/src/views/Me/DraftDetail/BottomBar.tsx index ce21c0f591..703e29093e 100644 --- a/src/views/Me/DraftDetail/BottomBar.tsx +++ b/src/views/Me/DraftDetail/BottomBar.tsx @@ -10,7 +10,7 @@ import { import BottomBar from '~/components/Editor/BottomBar' import SupportSettingDialog from '~/components/Editor/MoreSettings/SupportSettingDialog' import { - getSelectCampaign, + getSelectCampaigns, SelectCampaignProps, } from '~/components/Editor/SelectCampaign' import { SidebarIndentProps } from '~/components/Editor/Sidebar/Indent' @@ -71,8 +71,12 @@ const EditDraftBottomBar = ({ const hasOwnCircle = ownCircles && ownCircles.length >= 1 const tags = (draft.tags || []).map(toDigestTagPlaceholder) - const { appliedCampaign, selectedStage } = getSelectCampaign({ - applied: campaigns && campaigns[0], + const { + campaigns: selectableCampaigns, + selectedCampaign, + selectedStage, + } = getSelectCampaigns({ + applied: campaigns, attached: draft.campaigns, createdAt: draft.createdAt, }) @@ -124,9 +128,10 @@ const EditDraftBottomBar = ({ toggleIndent, indentSaving, - appliedCampaign, + campaigns: selectableCampaigns, + selectedCampaign, selectedStage, - editCampaign, + editCampaign: (value) => editCampaign(value as any), } return ( diff --git a/src/views/Me/DraftDetail/SettingsButton/index.tsx b/src/views/Me/DraftDetail/SettingsButton/index.tsx index f8f13e81ce..b9c177325f 100644 --- a/src/views/Me/DraftDetail/SettingsButton/index.tsx +++ b/src/views/Me/DraftDetail/SettingsButton/index.tsx @@ -10,7 +10,7 @@ import { SetTagsProps, } from '~/components/Editor' import { - getSelectCampaign, + getSelectCampaigns, SelectCampaignProps, } from '~/components/Editor/SelectCampaign' import { EditorSettingsDialog } from '~/components/Editor/SettingsDialog' @@ -130,16 +130,21 @@ const SettingsButton = ({ iscnPublishSaving, } - const { appliedCampaign, selectedStage } = getSelectCampaign({ - applied: campaigns && campaigns[0], + const { + campaigns: selectableCampaigns, + selectedCampaign, + selectedStage, + } = getSelectCampaigns({ + applied: campaigns, attached: draft.campaigns, createdAt: draft.createdAt, }) const campaignProps: Partial = { - appliedCampaign, + campaigns: selectableCampaigns, + selectedCampaign, selectedStage, - editCampaign, + editCampaign: (value) => editCampaign(value as any), } const responseProps: SetResponseProps = { diff --git a/src/views/Me/DraftDetail/Sidebar/index.tsx b/src/views/Me/DraftDetail/Sidebar/index.tsx index 816cf36e1f..a2f5b7e951 100644 --- a/src/views/Me/DraftDetail/Sidebar/index.tsx +++ b/src/views/Me/DraftDetail/Sidebar/index.tsx @@ -1,7 +1,7 @@ import { ENTITY_TYPE } from '~/common/enums' import { toDigestTagPlaceholder } from '~/components' import SupportSettingDialog from '~/components/Editor/MoreSettings/SupportSettingDialog' -import { getSelectCampaign } from '~/components/Editor/SelectCampaign' +import { getSelectCampaigns } from '~/components/Editor/SelectCampaign' import Sidebar from '~/components/Editor/Sidebar' import { DigestRichCirclePublicFragment, @@ -144,17 +144,22 @@ const EditDraftIndent = ({ draft }: SidebarProps) => { const EditDraftCampaign = ({ draft, campaigns }: SidebarProps) => { const { edit } = useEditDraftCampaign() - const { appliedCampaign, selectedStage } = getSelectCampaign({ - applied: campaigns && campaigns[0], + const { + campaigns: selectableCampaigns, + selectedCampaign, + selectedStage, + } = getSelectCampaigns({ + applied: campaigns, attached: draft.campaigns, createdAt: draft.createdAt, }) return ( edit(value as any)} /> ) } diff --git a/src/views/Me/DraftDetail/gql.ts b/src/views/Me/DraftDetail/gql.ts index bb11e35494..d720f84404 100644 --- a/src/views/Me/DraftDetail/gql.ts +++ b/src/views/Me/DraftDetail/gql.ts @@ -61,7 +61,7 @@ export const DRAFT_DETAIL_VIEWER = gql` query DraftDetailViewerQuery { viewer { id - campaigns(input: { first: 1 }) { + campaigns(input: { first: null }) { edges { node { id From 8deabbcda498d51e9052d60885b89193c99012aa Mon Sep 17 00:00:00 2001 From: Kechicode <186776112+Kechicode@users.noreply.github.com> Date: Fri, 6 Dec 2024 12:26:57 +0800 Subject: [PATCH 02/16] feat(Edit): enhance campaign selection UI and logic --- src/views/ArticleDetail/Edit/Header/index.tsx | 5 ++- src/views/ArticleDetail/Edit/gql.ts | 2 +- src/views/ArticleDetail/Edit/index.tsx | 38 ++++++++++++++----- 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/src/views/ArticleDetail/Edit/Header/index.tsx b/src/views/ArticleDetail/Edit/Header/index.tsx index 66e4ddddcc..f990cfea5d 100644 --- a/src/views/ArticleDetail/Edit/Header/index.tsx +++ b/src/views/ArticleDetail/Edit/Header/index.tsx @@ -97,10 +97,11 @@ const EditModeHeader = ({ const isSensitiveRevised = restProps.contentSensitive !== article.sensitiveByAuthor const isCampaignRevised = + restProps.selectedCampaign?.id !== article.campaigns[0].campaign.id || restProps.selectedStage !== article.campaigns[0]?.stage?.id const isResetCampaign = isCampaignRevised && - (!restProps.appliedCampaign?.id || !restProps.selectedStage) + (!restProps.selectedCampaign?.id || !restProps.selectedStage) const needRepublish = isTitleRevised || @@ -165,7 +166,7 @@ const EditModeHeader = ({ ? [] : [ { - campaign: restProps.appliedCampaign?.id, + campaign: restProps.selectedCampaign?.id, stage: restProps.selectedStage, }, ], diff --git a/src/views/ArticleDetail/Edit/gql.ts b/src/views/ArticleDetail/Edit/gql.ts index 914eb03b27..3926c0ea22 100644 --- a/src/views/ArticleDetail/Edit/gql.ts +++ b/src/views/ArticleDetail/Edit/gql.ts @@ -35,7 +35,7 @@ export const GET_EDIT_ARTICLE = gql` ownCircles { ...DigestRichCirclePublic } - campaigns(input: { first: 1 }) { + campaigns(input: { first: null }) { edges { node { id diff --git a/src/views/ArticleDetail/Edit/index.tsx b/src/views/ArticleDetail/Edit/index.tsx index 1b88e284ab..2cb1341de7 100644 --- a/src/views/ArticleDetail/Edit/index.tsx +++ b/src/views/ArticleDetail/Edit/index.tsx @@ -31,7 +31,7 @@ import { import BottomBar from '~/components/Editor/BottomBar' import SupportSettingDialog from '~/components/Editor/MoreSettings/SupportSettingDialog' import { - getSelectCampaign, + getSelectCampaigns, SelectCampaignProps, } from '~/components/Editor/SelectCampaign' import Sidebar from '~/components/Editor/Sidebar' @@ -53,6 +53,7 @@ import { DigestTagFragment, DirectImageUploadDoneMutation, DirectImageUploadMutation, + EditorSelectCampaignFragment, QueryEditArticleAssetsQuery, QueryEditArticleQuery, SingleFileUploadMutation, @@ -63,7 +64,7 @@ import EditHeader from './Header' import PublishState from './PublishState' import styles from './styles.module.css' -type Article = NonNullable< +export type Article = NonNullable< QueryEditArticleQuery['article'] & { __typename: 'Article' } @@ -142,21 +143,39 @@ const BaseEdit = ({ article }: { article: Article }) => { // campaign const appliedCampaigns = article.author.campaigns.edges?.map((e) => e.node) - const { appliedCampaign, selectedStage } = getSelectCampaign({ - applied: appliedCampaigns && appliedCampaigns[0], + const { + campaigns: selectableCampaigns, + selectedCampaign: _selectedCampaign, + selectedStage: _selectedStage, + } = getSelectCampaigns({ + applied: appliedCampaigns, attached: article.campaigns, createdAt: article.createdAt, }) const [campaign, setCampaign] = useState( - appliedCampaign?.id && selectedStage + _selectedCampaign?.id && _selectedStage ? { - campaign: appliedCampaign.id, - stage: selectedStage, + campaign: _selectedCampaign.id, + stage: _selectedStage, } : undefined ) + const [selectedCampaign, setSelectedCampaign] = useState< + EditorSelectCampaignFragment | undefined + >(_selectedCampaign) + const [selectedStage, setSelectedStage] = useState( + _selectedStage + ) + + useEffect(() => { + setSelectedCampaign( + selectableCampaigns?.find((c) => c.id === campaign?.campaign) + ) + setSelectedStage(campaign?.stage || undefined) + }, [campaign]) + const [requestForDonation, setRequestForDonation] = useState( article.requestForDonation ) @@ -212,8 +231,9 @@ const BaseEdit = ({ article }: { article: Article }) => { indentSaving: false, } const campaignProps: Partial = { - appliedCampaign, - selectedStage: campaign?.stage, + campaigns: selectableCampaigns, + selectedCampaign: selectedCampaign, + selectedStage: selectedStage, editCampaign: setCampaign, } From 5f67c9691a0c2ce56e87c45f6ae7d472f1d918f7 Mon Sep 17 00:00:00 2001 From: Kechicode <186776112+Kechicode@users.noreply.github.com> Date: Fri, 6 Dec 2024 15:27:10 +0800 Subject: [PATCH 03/16] feat(SelectCampaign): remove redundant code --- .../Editor/SelectCampaign/index.tsx | 97 ++++++++----------- 1 file changed, 42 insertions(+), 55 deletions(-) diff --git a/src/components/Editor/SelectCampaign/index.tsx b/src/components/Editor/SelectCampaign/index.tsx index e5cd71d680..0761f901ca 100644 --- a/src/components/Editor/SelectCampaign/index.tsx +++ b/src/components/Editor/SelectCampaign/index.tsx @@ -1,5 +1,5 @@ import gql from 'graphql-tag' -import { useContext, useEffect, useState } from 'react' +import { useContext } from 'react' import { FormattedMessage } from 'react-intl' import { datetimeFormat } from '~/common/utils' @@ -60,33 +60,21 @@ const SelectCampaign = ({ selectedStage, editCampaign, }: SelectCampaignProps) => { - const [selectedCampaignId, setSelectedCampaignId] = useState< - string | undefined - >(selectedCampaign?.id) - const [selectedStageId, setSelectedStageId] = useState( - selectedStage - ) - - useEffect(() => { - setSelectedCampaignId(selectedCampaign?.id) - setSelectedStageId(selectedStage) - }, [selectedCampaign, selectedStage]) - const { lang } = useContext(LanguageContext) const RESET_CAMPAIGN_OPTION = { name: , value: undefined, - selected: !selectedCampaignId, + selected: !selectedCampaign?.id, } const RESET_STAGE_OPTION = { name: , value: undefined, - selected: !selectedStageId, + selected: !selectedStage, } const now = new Date() - const availableStages = selectedCampaignId + const availableStages = selectedCampaign?.id ? campaigns - .find((c) => c.id === selectedCampaignId) + .find((c) => c.id === selectedCampaign.id) ?.stages.filter((s) => { const period = s.period @@ -101,8 +89,6 @@ const SelectCampaign = ({ name="select-campaign" onChange={(option) => { - setSelectedCampaignId(option.value) - setSelectedStageId(undefined) editCampaign( option.value !== undefined ? { campaign: option.value } : undefined ) @@ -113,48 +99,49 @@ const SelectCampaign = ({ return { name: c.name, value: c.id, - selected: c.id === selectedCampaignId, + selected: c.id === selectedCampaign?.id, } }), ]} size={14} color="freeWriteBlue" /> - {selectedCampaignId && availableStages && availableStages.length > 0 && ( - <> - - - name="select-stage" - onChange={(option) => { - setSelectedStageId(option.value) - editCampaign( - option.value - ? { campaign: selectedCampaignId, stage: option.value } - : { campaign: selectedCampaignId } - ) - }} - options={[ - RESET_STAGE_OPTION, - ...availableStages.reverse().map((s) => { - return { - name: s.period?.start - ? `${s.name} - ${datetimeFormat.absolute({ - date: s.period.start, - lang, - optionalYear: false, - utc8: true, - })}` - : s.name, - value: s.id, - selected: s.id === selectedStageId, - } - }), - ]} - size={14} - color="freeWriteBlue" - /> - - )} + {selectedCampaign?.id && + availableStages && + availableStages.length > 0 && ( + <> + + + name="select-stage" + onChange={(option) => { + editCampaign( + option.value + ? { campaign: selectedCampaign.id, stage: option.value } + : { campaign: selectedCampaign.id } + ) + }} + options={[ + RESET_STAGE_OPTION, + ...availableStages.reverse().map((s) => { + return { + name: s.period?.start + ? `${s.name} - ${datetimeFormat.absolute({ + date: s.period.start, + lang, + optionalYear: false, + utc8: true, + })}` + : s.name, + value: s.id, + selected: s.id === selectedStage, + } + }), + ]} + size={14} + color="freeWriteBlue" + /> + + )} ) } From 7fcbaba5648a44f0999b6906cf421a4857fd4102 Mon Sep 17 00:00:00 2001 From: Kechicode <186776112+Kechicode@users.noreply.github.com> Date: Fri, 6 Dec 2024 15:51:01 +0800 Subject: [PATCH 04/16] fix(ArticleDetail): handle optional campaign check in article edit header --- src/views/ArticleDetail/Edit/Header/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/ArticleDetail/Edit/Header/index.tsx b/src/views/ArticleDetail/Edit/Header/index.tsx index f990cfea5d..105271b6e0 100644 --- a/src/views/ArticleDetail/Edit/Header/index.tsx +++ b/src/views/ArticleDetail/Edit/Header/index.tsx @@ -97,7 +97,7 @@ const EditModeHeader = ({ const isSensitiveRevised = restProps.contentSensitive !== article.sensitiveByAuthor const isCampaignRevised = - restProps.selectedCampaign?.id !== article.campaigns[0].campaign.id || + restProps.selectedCampaign?.id !== article.campaigns[0]?.campaign.id || restProps.selectedStage !== article.campaigns[0]?.stage?.id const isResetCampaign = isCampaignRevised && From 8c88bace3cfe3000bbb3950d28d5d716cac4ff9f Mon Sep 17 00:00:00 2001 From: Kechicode <186776112+Kechicode@users.noreply.github.com> Date: Fri, 6 Dec 2024 15:57:54 +0800 Subject: [PATCH 05/16] refactor(ArticleDetail): extract campaign state logic into custom hook --- .../Edit/Hooks/useCampaignState.ts | 56 +++++++++++++++++++ src/views/ArticleDetail/Edit/index.tsx | 48 ++-------------- 2 files changed, 62 insertions(+), 42 deletions(-) create mode 100644 src/views/ArticleDetail/Edit/Hooks/useCampaignState.ts diff --git a/src/views/ArticleDetail/Edit/Hooks/useCampaignState.ts b/src/views/ArticleDetail/Edit/Hooks/useCampaignState.ts new file mode 100644 index 0000000000..5fa16c298d --- /dev/null +++ b/src/views/ArticleDetail/Edit/Hooks/useCampaignState.ts @@ -0,0 +1,56 @@ +import { useEffect, useState } from 'react' + +import { getSelectCampaigns } from '~/components/Editor/SelectCampaign' +import { + ArticleCampaignInput, + EditorSelectCampaignFragment, +} from '~/gql/graphql' + +import { Article } from '../index' + +export const useCampaignState = (article: Article) => { + const appliedCampaigns = article.author.campaigns.edges?.map((e) => e.node) + + const { + campaigns: selectableCampaigns, + selectedCampaign: initialSelectedCampaign, + selectedStage: initialSelectedStage, + } = getSelectCampaigns({ + applied: appliedCampaigns, + attached: article.campaigns, + createdAt: article.createdAt, + }) + + const [campaign, setCampaign] = useState( + initialSelectedCampaign?.id && initialSelectedStage + ? { + campaign: initialSelectedCampaign.id, + stage: initialSelectedStage, + } + : undefined + ) + + // UI state for selected campaign/stage + const [selectedCampaign, setSelectedCampaign] = useState< + EditorSelectCampaignFragment | undefined + >(initialSelectedCampaign) + const [selectedStage, setSelectedStage] = useState( + initialSelectedStage + ) + + // Keep UI state in sync with campaign input + useEffect(() => { + setSelectedCampaign( + selectableCampaigns?.find((c) => c.id === campaign?.campaign) + ) + setSelectedStage(campaign?.stage || undefined) + }, [campaign, selectableCampaigns]) + + return { + campaign, + setCampaign, + selectedCampaign, + selectedStage, + selectableCampaigns, + } +} diff --git a/src/views/ArticleDetail/Edit/index.tsx b/src/views/ArticleDetail/Edit/index.tsx index 2cb1341de7..3f1f8d4005 100644 --- a/src/views/ArticleDetail/Edit/index.tsx +++ b/src/views/ArticleDetail/Edit/index.tsx @@ -30,10 +30,7 @@ import { } from '~/components/Editor' import BottomBar from '~/components/Editor/BottomBar' import SupportSettingDialog from '~/components/Editor/MoreSettings/SupportSettingDialog' -import { - getSelectCampaigns, - SelectCampaignProps, -} from '~/components/Editor/SelectCampaign' +import { SelectCampaignProps } from '~/components/Editor/SelectCampaign' import Sidebar from '~/components/Editor/Sidebar' import { SidebarIndentProps } from '~/components/Editor/Sidebar/Indent' import { QueryError, useImperativeQuery } from '~/components/GQL' @@ -44,7 +41,6 @@ import { } from '~/components/GQL/mutations/uploadFile' import { ArticleAccessType, - ArticleCampaignInput, ArticleDigestDropdownArticleFragment, ArticleLicenseType, AssetFragment, @@ -53,7 +49,6 @@ import { DigestTagFragment, DirectImageUploadDoneMutation, DirectImageUploadMutation, - EditorSelectCampaignFragment, QueryEditArticleAssetsQuery, QueryEditArticleQuery, SingleFileUploadMutation, @@ -61,6 +56,7 @@ import { import { GET_EDIT_ARTICLE, GET_EDIT_ARTICLE_ASSETS } from './gql' import EditHeader from './Header' +import { useCampaignState } from './Hooks/useCampaignState' import PublishState from './PublishState' import styles from './styles.module.css' @@ -141,40 +137,8 @@ const BaseEdit = ({ article }: { article: Article }) => { setLicense(newLicense) } - // campaign - const appliedCampaigns = article.author.campaigns.edges?.map((e) => e.node) - const { - campaigns: selectableCampaigns, - selectedCampaign: _selectedCampaign, - selectedStage: _selectedStage, - } = getSelectCampaigns({ - applied: appliedCampaigns, - attached: article.campaigns, - createdAt: article.createdAt, - }) - - const [campaign, setCampaign] = useState( - _selectedCampaign?.id && _selectedStage - ? { - campaign: _selectedCampaign.id, - stage: _selectedStage, - } - : undefined - ) - - const [selectedCampaign, setSelectedCampaign] = useState< - EditorSelectCampaignFragment | undefined - >(_selectedCampaign) - const [selectedStage, setSelectedStage] = useState( - _selectedStage - ) - - useEffect(() => { - setSelectedCampaign( - selectableCampaigns?.find((c) => c.id === campaign?.campaign) - ) - setSelectedStage(campaign?.stage || undefined) - }, [campaign]) + const { setCampaign, selectedCampaign, selectedStage, selectableCampaigns } = + useCampaignState(article) const [requestForDonation, setRequestForDonation] = useState( article.requestForDonation @@ -232,8 +196,8 @@ const BaseEdit = ({ article }: { article: Article }) => { } const campaignProps: Partial = { campaigns: selectableCampaigns, - selectedCampaign: selectedCampaign, - selectedStage: selectedStage, + selectedCampaign, + selectedStage, editCampaign: setCampaign, } From d9d67128d1ffdd1e3d2d37c132820809d1ecd8d0 Mon Sep 17 00:00:00 2001 From: Kechicode <186776112+Kechicode@users.noreply.github.com> Date: Fri, 6 Dec 2024 17:26:22 +0800 Subject: [PATCH 06/16] fix(lang): fix copy --- lang/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lang/en.json b/lang/en.json index 3db4d8b52d..828e86a159 100644 --- a/lang/en.json +++ b/lang/en.json @@ -529,7 +529,7 @@ "defaultMessage": "Upload file" }, "6pc948": { - "defaultMessage": "Add to FreeWrite" + "defaultMessage": "Add to Free Write" }, "6q0G5e": { "defaultMessage": "Successfully added", From dc209d02ac0750c57598a6acb43f28829e31c55a Mon Sep 17 00:00:00 2001 From: gitwoz <177856586+gitwoz@users.noreply.github.com> Date: Thu, 5 Dec 2024 09:17:52 +0700 Subject: [PATCH 07/16] feat(cli): run gen:type:prod for "release/*" --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d0c625f838..43c73ac174 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "storybook": "storybook dev -p 6006", "build-storybook": "storybook build", "prepare": "husky install", - "vercel-build": "set -xe; npm run gen:type && if [[ \"$NEXT_PUBLIC_VERCEL_GIT_COMMIT_REF\" =~ release/* ]] ; then cp -va .env.prod .env.local ; echo 'NEXT_PUBLIC_SITE_DOMAIN=web-next.matters.town' | tee -a .env.local; else cp -va .env.dev .env.local ; echo 'NEXT_PUBLIC_SITE_DOMAIN=web-dev.matters.town' | tee -a .env.local ; fi && { echo 'NEXT_PUBLIC_NEXT_ASSET_DOMAIN='; echo 'NEXT_PUBLIC_ADMIN_VIEW=true'; } | tee -a .env.local && npm run build", + "vercel-build": "set -xe; if [[ \"$NEXT_PUBLIC_VERCEL_GIT_COMMIT_REF\" =~ release/* ]] ; then npm run gen:type:prod && cp -va .env.prod .env.local ; echo 'NEXT_PUBLIC_SITE_DOMAIN=web-next.matters.town' | tee -a .env.local; else npm run gen:type && cp -va .env.dev .env.local ; echo 'NEXT_PUBLIC_SITE_DOMAIN=web-dev.matters.town' | tee -a .env.local ; fi && { echo 'NEXT_PUBLIC_NEXT_ASSET_DOMAIN='; echo 'NEXT_PUBLIC_ADMIN_VIEW=true'; } | tee -a .env.local && npm run build", "i18n:extract": "formatjs extract 'src/**/*.ts*' --ignore='**/*.d.ts' --id-interpolation-pattern '[sha512:contenthash:base64:6]' --out-file lang/default.json", "i18n:generate": "node bin/i18nGenerate.js", "i18n:compile": "formatjs compile-folder --ast lang compiled-lang", From 2debb09b48728b70a90fa369ebac1991e09cc1a2 Mon Sep 17 00:00:00 2001 From: gitwoz <177856586+gitwoz@users.noreply.github.com> Date: Tue, 10 Dec 2024 08:34:16 +0700 Subject: [PATCH 08/16] fix(tag): fix recommended tags query on editor since the permission changes --- .../RecommendedTags/index.tsx | 2 +- .../TagCustomStagingArea/SelectedTags/index.tsx | 2 +- .../Editor/TagCustomStagingArea/gql.ts | 4 ++-- .../Editor/TagCustomStagingArea/index.tsx | 17 ++++++----------- 4 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/components/Editor/TagCustomStagingArea/RecommendedTags/index.tsx b/src/components/Editor/TagCustomStagingArea/RecommendedTags/index.tsx index 9330743df0..cac5fe7747 100644 --- a/src/components/Editor/TagCustomStagingArea/RecommendedTags/index.tsx +++ b/src/components/Editor/TagCustomStagingArea/RecommendedTags/index.tsx @@ -7,7 +7,7 @@ import { EditorRecommendedTagsQuery } from '~/gql/graphql' import styles from './styles.module.css' type EditorRecommendedTagsUserTagsEdgesNode = NonNullable< - NonNullable['tags']['edges'] + NonNullable['tags']['edges'] >[0]['node'] & { __typename: 'Tag' } type RecommendedTagsProps = { diff --git a/src/components/Editor/TagCustomStagingArea/SelectedTags/index.tsx b/src/components/Editor/TagCustomStagingArea/SelectedTags/index.tsx index bcbe940121..5a90ddeee3 100644 --- a/src/components/Editor/TagCustomStagingArea/SelectedTags/index.tsx +++ b/src/components/Editor/TagCustomStagingArea/SelectedTags/index.tsx @@ -5,7 +5,7 @@ import { EditorRecommendedTagsQuery } from '~/gql/graphql' import styles from './styles.module.css' type EditorRecommendedTagsUserTagsEdgesNode = NonNullable< - NonNullable['tags']['edges'] + NonNullable['tags']['edges'] >[0]['node'] & { __typename: 'Tag' } type SelectedTagsProps = { diff --git a/src/components/Editor/TagCustomStagingArea/gql.ts b/src/components/Editor/TagCustomStagingArea/gql.ts index fcdcdb5079..a16ce12cc4 100644 --- a/src/components/Editor/TagCustomStagingArea/gql.ts +++ b/src/components/Editor/TagCustomStagingArea/gql.ts @@ -3,8 +3,8 @@ import gql from 'graphql-tag' import { ListTag } from '~/components/Tag' export const EDITOR_RECOMMENDED_TAGS = gql` - query EditorRecommendedTags($userName: String!) { - user(input: { userName: $userName }) { + query EditorRecommendedTags { + viewer { id tags(input: { first: 10 }) { edges { diff --git a/src/components/Editor/TagCustomStagingArea/index.tsx b/src/components/Editor/TagCustomStagingArea/index.tsx index 15b8d91a78..4d5d301e2b 100644 --- a/src/components/Editor/TagCustomStagingArea/index.tsx +++ b/src/components/Editor/TagCustomStagingArea/index.tsx @@ -1,8 +1,8 @@ +import { useQuery } from '@apollo/react-hooks' import _uniqBy from 'lodash/uniqBy' -import { useContext } from 'react' import { MAX_ARTICLE_TAG_LENGTH } from '~/common/enums' -import { SpinnerBlock, usePublicQuery, ViewerContext } from '~/components' +import { SpinnerBlock } from '~/components' import { SelectTag } from '~/components/SearchSelect/SearchingArea' import { CustomStagingAreaProps } from '~/components/SearchSelect/StagingArea' import { EditorRecommendedTagsQuery } from '~/gql/graphql' @@ -14,7 +14,7 @@ import styles from './styles.module.css' type EditorRecommendedTagsUserTagsEdgesNode = Required< NonNullable< - NonNullable['tags']['edges'] + NonNullable['tags']['edges'] >[0]['node'] > @@ -24,21 +24,16 @@ const TagCustomStagingArea = ({ hint, toStagingArea, }: CustomStagingAreaProps) => { - const viewer = useContext(ViewerContext) - /** * Data Fetching */ // public data - const { data, loading } = usePublicQuery( - EDITOR_RECOMMENDED_TAGS, - { - variables: { userName: viewer.userName }, - } + const { data, loading } = useQuery( + EDITOR_RECOMMENDED_TAGS ) // recommended tags - const userTagsEdges = data?.user?.tags.edges || [] + const userTagsEdges = data?.viewer?.tags.edges || [] let recommendedTags = [...userTagsEdges]?.map((edge) => edge.node) // remove duplicated tags From 9de2666f0847df453d63f9d4042da4630e85c92e Mon Sep 17 00:00:00 2001 From: Kechicode <186776112+Kechicode@users.noreply.github.com> Date: Tue, 10 Dec 2024 14:54:35 +0800 Subject: [PATCH 09/16] fix(Editor): show campaign section only when campaigns exist ref: PD-2412-1-6 --- src/components/Editor/BottomBar/MobileSettingsDialog/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Editor/BottomBar/MobileSettingsDialog/index.tsx b/src/components/Editor/BottomBar/MobileSettingsDialog/index.tsx index 5b9b9918d1..4ee68a118a 100644 --- a/src/components/Editor/BottomBar/MobileSettingsDialog/index.tsx +++ b/src/components/Editor/BottomBar/MobileSettingsDialog/index.tsx @@ -63,7 +63,7 @@ const BaseMobileSettingsDialog = ({ {/* campaign */} - {campaigns && selectedCampaign && editCampaign && ( + {campaigns && campaigns.length > 0 && editCampaign && (

Date: Tue, 10 Dec 2024 15:47:23 +0800 Subject: [PATCH 10/16] fix(ArticleDetail): Simplify campaign state initialization in useCampaignState hook PD-2412-1-4 --- src/views/ArticleDetail/Edit/Hooks/useCampaignState.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/ArticleDetail/Edit/Hooks/useCampaignState.ts b/src/views/ArticleDetail/Edit/Hooks/useCampaignState.ts index 5fa16c298d..f117621365 100644 --- a/src/views/ArticleDetail/Edit/Hooks/useCampaignState.ts +++ b/src/views/ArticleDetail/Edit/Hooks/useCampaignState.ts @@ -22,7 +22,7 @@ export const useCampaignState = (article: Article) => { }) const [campaign, setCampaign] = useState( - initialSelectedCampaign?.id && initialSelectedStage + initialSelectedCampaign?.id ? { campaign: initialSelectedCampaign.id, stage: initialSelectedStage, From f0ecca6b9b814f43e17d4edc61cbf14b2c59554c Mon Sep 17 00:00:00 2001 From: Zeck Li <11781254+zeckli@users.noreply.github.com> Date: Tue, 10 Dec 2024 22:02:39 +0800 Subject: [PATCH 11/16] fix(tracker): fix read timer does not work if user bounces immediately --- src/components/Hook/useReadTimer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Hook/useReadTimer.ts b/src/components/Hook/useReadTimer.ts index 67cfd0389a..02f4a3efb2 100644 --- a/src/components/Hook/useReadTimer.ts +++ b/src/components/Hook/useReadTimer.ts @@ -25,7 +25,7 @@ export const useReadTimer = ({ articleId, container }: Props) => { }, 3000) const storeReadTime = () => { - if (articleId && readTimer?.current) + if (articleId && readTimer) analytics.trackEvent('read_time', { articleId, time: readTimer.current, From 8b80d28ce42a27f3b2004491d14afb2fd16e662b Mon Sep 17 00:00:00 2001 From: gitwoz <177856586+gitwoz@users.noreply.github.com> Date: Wed, 11 Dec 2024 09:52:12 +0700 Subject: [PATCH 12/16] fix(campaign): correct article label for non-announcement --- .../ArticleFeeds/MainFeed/gql.ts | 1 + .../ArticleFeeds/MainFeed/index.tsx | 22 ++++++++++++------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/views/CampaignDetail/ArticleFeeds/MainFeed/gql.ts b/src/views/CampaignDetail/ArticleFeeds/MainFeed/gql.ts index 7a6508b378..0b87aa36af 100644 --- a/src/views/CampaignDetail/ArticleFeeds/MainFeed/gql.ts +++ b/src/views/CampaignDetail/ArticleFeeds/MainFeed/gql.ts @@ -21,6 +21,7 @@ export const CAMPAIGN_ARTICLES_PUBLIC = gql` edges { cursor featured + announcement node { id campaigns { diff --git a/src/views/CampaignDetail/ArticleFeeds/MainFeed/index.tsx b/src/views/CampaignDetail/ArticleFeeds/MainFeed/index.tsx index 148a6de4f2..30185f0b7e 100644 --- a/src/views/CampaignDetail/ArticleFeeds/MainFeed/index.tsx +++ b/src/views/CampaignDetail/ArticleFeeds/MainFeed/index.tsx @@ -49,17 +49,21 @@ const getArticleStage = (article: CampaignArticlesPublicQueryArticle) => { return stage } -const getArticleStageName = ( +const getLabel = ( article: CampaignArticlesPublicQueryArticle, - lang: string + lang: string, + announcement: boolean ) => { const stage = getArticleStage(article) - // announcement if nullish - if (!stage) { + if (announcement) { return } + if (!stage) { + return '' + } + return stage[ `name${lang === 'en' ? 'En' : lang === 'zh-Hans' ? 'ZhHans' : 'ZhHant'}` ] @@ -218,20 +222,22 @@ const MainFeed = ({ feedType, camapign }: MainFeedProps) => { return ( - {edges.map(({ node, featured }, i) => ( + {edges.map(({ node, featured, announcement }, i) => ( - {(isAll || isFeatured) && ( + {(isAll || + isFeatured || + getLabel(node, lang, announcement)) && ( - {getArticleStageName(node, lang)} + {getLabel(node, lang, announcement)} )} {!isFeatured && featured && } From 4776b16cd941b850eb2b7f0a031c3955b1cf7e3c Mon Sep 17 00:00:00 2001 From: Kechicode <186776112+Kechicode@users.noreply.github.com> Date: Wed, 11 Dec 2024 14:40:22 +0800 Subject: [PATCH 13/16] fix(ArticleDetail): Add announcement flag to campaign detail routing --- src/common/utils/route.ts | 3 ++- src/views/ArticleDetail/Header/gql.ts | 3 +++ src/views/ArticleDetail/Header/index.tsx | 4 ++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/common/utils/route.ts b/src/common/utils/route.ts index e55ae0d234..d815ab0930 100644 --- a/src/common/utils/route.ts +++ b/src/common/utils/route.ts @@ -94,6 +94,7 @@ type ToPathArgs = campaign: CampaignArgs stage?: CampaignStageArgs featured?: boolean + announcement?: boolean } | { page: 'userProfile' | 'userCollections' @@ -247,7 +248,7 @@ export const toPath = ( href = `${href}?type=${args.stage.id}` } else if (args.featured) { href = `${href}?type=featured` - } else { + } else if (args.announcement) { href = `${href}?type=announcement` } break diff --git a/src/views/ArticleDetail/Header/gql.ts b/src/views/ArticleDetail/Header/gql.ts index 1bbed3ca0c..ab30f43887 100644 --- a/src/views/ArticleDetail/Header/gql.ts +++ b/src/views/ArticleDetail/Header/gql.ts @@ -13,6 +13,9 @@ export const fragments = { nameZhHant: name(input: { language: zh_hant }) nameZhHans: name(input: { language: zh_hans }) nameEn: name(input: { language: en }) + announcements { + id + } } } stage { diff --git a/src/views/ArticleDetail/Header/index.tsx b/src/views/ArticleDetail/Header/index.tsx index 47d1077a00..fec515b8b7 100644 --- a/src/views/ArticleDetail/Header/index.tsx +++ b/src/views/ArticleDetail/Header/index.tsx @@ -22,6 +22,9 @@ const Header = ({ article }: HeaderProps) => { const campaign = article.campaigns[0]?.campaign const campaignStage = article.campaigns[0]?.stage const { lang } = useContext(LanguageContext) + const isAnnouncement = article.campaigns[0]?.campaign?.announcements?.some( + (announcement: { id: string }) => announcement.id === article.id + ) return (
@@ -36,6 +39,7 @@ const Header = ({ article }: HeaderProps) => { page: 'campaignDetail', campaign, stage: campaignStage || undefined, + announcement: isAnnouncement, }).href } onClick={() => { From 43fc2d5ebb9d4e9f8a3de2247712c3277b33907a Mon Sep 17 00:00:00 2001 From: Kechicode <186776112+Kechicode@users.noreply.github.com> Date: Thu, 12 Dec 2024 10:04:29 +0800 Subject: [PATCH 14/16] fix(lang): fix copy --- lang/zh-Hans.json | 2 +- lang/zh-Hant.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lang/zh-Hans.json b/lang/zh-Hans.json index 377a667420..011fb4c6cf 100644 --- a/lang/zh-Hans.json +++ b/lang/zh-Hans.json @@ -2754,7 +2754,7 @@ "description": "src/components/Forms/CreateCircleForm/Profile.tsx" }, "m1wKuC": { - "defaultMessage": "参与七日书活动" + "defaultMessage": "参与活动" }, "m4GG4b": { "defaultMessage": "删除选集" diff --git a/lang/zh-Hant.json b/lang/zh-Hant.json index d08e228708..3fc2c247c0 100644 --- a/lang/zh-Hant.json +++ b/lang/zh-Hant.json @@ -2754,7 +2754,7 @@ "description": "src/components/Forms/CreateCircleForm/Profile.tsx" }, "m1wKuC": { - "defaultMessage": "參與七日書活動" + "defaultMessage": "參與活動" }, "m4GG4b": { "defaultMessage": "刪除選集" From f21270781c41824ee7652cbde7a0db6bb29cf8f4 Mon Sep 17 00:00:00 2001 From: Kechicode <186776112+Kechicode@users.noreply.github.com> Date: Thu, 12 Dec 2024 15:13:22 +0800 Subject: [PATCH 15/16] fix(CampaignDetail): handle campaigns without end date in apply button CC-2412-2-3 --- src/views/CampaignDetail/Apply/Button/index.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/views/CampaignDetail/Apply/Button/index.tsx b/src/views/CampaignDetail/Apply/Button/index.tsx index cc52edcbe9..b811fceba5 100644 --- a/src/views/CampaignDetail/Apply/Button/index.tsx +++ b/src/views/CampaignDetail/Apply/Button/index.tsx @@ -34,7 +34,8 @@ const ApplyCampaignButton = ({ const isRejected = applicationState === 'rejected' const isNotApplied = !applicationState const isAppliedDuringPeriod = - appliedAt && new Date(appliedAt) <= new Date(appEnd) + (appliedAt && appEnd && new Date(appliedAt) <= new Date(appEnd)) || + (appliedAt && !appEnd) const isApplicationStarted = now >= new Date(appStart) const isActiveCampaign = campaign.state === 'active' From 705ee134832b1ee4fb7bc634748ae53073b85bad Mon Sep 17 00:00:00 2001 From: Woz <177856586+gitwoz@users.noreply.github.com> Date: Thu, 12 Dec 2024 15:56:51 +0800 Subject: [PATCH 16/16] chore(release): v5.7.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 43c73ac174..ecf206f0ac 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matters-web", - "version": "5.7.0", + "version": "5.7.1", "description": "codebase of Matters' website", "author": "Matters ", "engines": {