From 0324712248b7208719878de75f7706cdeb4cb730 Mon Sep 17 00:00:00 2001 From: Cezary Dryl Date: Tue, 1 Oct 2024 10:05:24 +0200 Subject: [PATCH] feat: added reputation method to release --- .../PaymentBuilderWidget.tsx | 62 ++----- .../partials/PaymentBuilderWidget/hooks.tsx | 164 ++++++++++++++++++ .../partials/PaymentBuilderWidget/types.ts | 8 +- .../ReleasePaymentModal.tsx | 30 +++- .../partials/ReleasePaymentModal/consts.ts | 3 + .../partials/ReleasePaymentModal/hooks.ts | 12 ++ .../ReleaseRequests/ReleaseRequestItem.tsx | 61 +++++++ .../ReleaseRequests/ReleaseRequests.tsx | 37 ++++ .../partials/ReleaseRequests/types.ts | 9 + .../PaymentBuilderContext.ts | 4 + .../PaymentBuilderContextProvider.tsx | 6 + src/graphql/fragments/expenditures.graphql | 5 +- src/graphql/generated.ts | 2 +- src/i18n/en.json | 1 + 14 files changed, 347 insertions(+), 57 deletions(-) create mode 100644 src/components/v5/common/CompletedAction/partials/PaymentBuilder/partials/PaymentBuilderWidget/hooks.tsx create mode 100644 src/components/v5/common/CompletedAction/partials/PaymentBuilder/partials/ReleaseRequests/ReleaseRequestItem.tsx create mode 100644 src/components/v5/common/CompletedAction/partials/PaymentBuilder/partials/ReleaseRequests/ReleaseRequests.tsx create mode 100644 src/components/v5/common/CompletedAction/partials/PaymentBuilder/partials/ReleaseRequests/types.ts diff --git a/src/components/v5/common/CompletedAction/partials/PaymentBuilder/partials/PaymentBuilderWidget/PaymentBuilderWidget.tsx b/src/components/v5/common/CompletedAction/partials/PaymentBuilder/partials/PaymentBuilderWidget/PaymentBuilderWidget.tsx index d86a922da9..d50346aaab 100644 --- a/src/components/v5/common/CompletedAction/partials/PaymentBuilder/partials/PaymentBuilderWidget/PaymentBuilderWidget.tsx +++ b/src/components/v5/common/CompletedAction/partials/PaymentBuilder/partials/PaymentBuilderWidget/PaymentBuilderWidget.tsx @@ -25,7 +25,6 @@ import { type StepperItem } from '~v5/shared/Stepper/types.ts'; import ActionWithPermissionsInfo from '../ActionWithPermissionsInfo/ActionWithPermissionsInfo.tsx'; import ActionWithStakingInfo from '../ActionWithStakingInfo/ActionWithStakingInfo.tsx'; -import FinalizeByPaymentCreatorInfo from '../FinalizeByPaymentCreatorInfo/FinalizeByPaymentCreatorInfo.tsx'; import FundingModal from '../FundingModal/FundingModal.tsx'; import FundingRequests from '../FundingRequests/FundingRequests.tsx'; import MotionBox from '../MotionBox/MotionBox.tsx'; @@ -33,6 +32,7 @@ import PaymentStepDetailsBlock from '../PaymentStepDetailsBlock/PaymentStepDetai import ReleasePaymentModal from '../ReleasePaymentModal/ReleasePaymentModal.tsx'; import StepDetailsBlock from '../StepDetailsBlock/StepDetailsBlock.tsx'; +import { useGetReleaseStep } from './hooks.tsx'; import { ExpenditureStep, type PaymentBuilderWidgetProps } from './types.ts'; import { getCancelStepIndex, @@ -56,7 +56,6 @@ const PaymentBuilderWidget: FC = ({ action }) => { toggleOnFundingModal: showFundingModal, isReleaseModalOpen: isReleasePaymentModalOpen, toggleOffReleaseModal: hideReleasePaymentModal, - toggleOnReleaseModal: showReleasePaymentModal, selectedFundingAction, setSelectedFundingAction, } = usePaymentBuilderContext(); @@ -66,15 +65,8 @@ const PaymentBuilderWidget: FC = ({ action }) => { const { expenditure, loadingExpenditure, startPolling, stopPolling } = useGetExpenditureData(expenditureId); - const { - fundingActions, - finalizingActions, - cancellingActions, - finalizedAt, - isStaked, - userStake, - ownerAddress, - } = expenditure || {}; + const { fundingActions, cancellingActions, isStaked, userStake } = + expenditure || {}; const { amount: stakeAmount = '' } = userStake || {}; const expenditureStep = getExpenditureStep(expenditure); @@ -107,6 +99,12 @@ const PaymentBuilderWidget: FC = ({ action }) => { } }, [expectedStepKey, expenditureStep]); + const releaseStep = useGetReleaseStep({ + expectedStepKey, + expenditure, + expenditureStep, + }); + const lockExpenditurePayload: LockExpenditurePayload | null = useMemo( () => expenditure @@ -302,47 +300,7 @@ const PaymentBuilderWidget: FC = ({ action }) => { ), }, - { - key: ExpenditureStep.Release, - heading: { label: formatText({ id: 'expenditure.releaseStage.label' }) }, - content: - expenditureStep === ExpenditureStep.Release ? ( - - } - /> - ) : ( - <> - {finalizedAt ? ( - <> - {finalizingActions?.items[0]?.initiatorAddress === - ownerAddress ? ( - - ) : ( - - )} - - ) : ( -
- )} - - ), - }, + releaseStep, { key: ExpenditureStep.Payment, heading: { label: formatText({ id: 'expenditure.paymentStage.label' }) }, diff --git a/src/components/v5/common/CompletedAction/partials/PaymentBuilder/partials/PaymentBuilderWidget/hooks.tsx b/src/components/v5/common/CompletedAction/partials/PaymentBuilder/partials/PaymentBuilderWidget/hooks.tsx new file mode 100644 index 0000000000..da9da15cf6 --- /dev/null +++ b/src/components/v5/common/CompletedAction/partials/PaymentBuilder/partials/PaymentBuilderWidget/hooks.tsx @@ -0,0 +1,164 @@ +import { SpinnerGap } from '@phosphor-icons/react'; +import React, { useEffect, useMemo } from 'react'; + +import { usePaymentBuilderContext } from '~context/PaymentBuilderContext/PaymentBuilderContext.ts'; +import { notMaybe, notNull } from '~utils/arrays/index.ts'; +import { MotionState } from '~utils/colonyMotions.ts'; +import { formatText } from '~utils/intl.ts'; +import useGetColonyAction from '~v5/common/ActionSidebar/hooks/useGetColonyAction.ts'; +import MotionCountDownTimer from '~v5/common/ActionSidebar/partials/Motions/partials/MotionCountDownTimer/MotionCountDownTimer.tsx'; +import Button from '~v5/shared/Button/Button.tsx'; +import IconButton from '~v5/shared/Button/IconButton.tsx'; + +import ActionWithPermissionsInfo from '../ActionWithPermissionsInfo/ActionWithPermissionsInfo.tsx'; +import FinalizeByPaymentCreatorInfo from '../FinalizeByPaymentCreatorInfo/FinalizeByPaymentCreatorInfo.tsx'; +import MotionBox from '../MotionBox/MotionBox.tsx'; +import ReleaseRequests from '../ReleaseRequests/ReleaseRequests.tsx'; +import StepDetailsBlock from '../StepDetailsBlock/StepDetailsBlock.tsx'; + +import { ExpenditureStep, type ReleaseStepProps } from './types.ts'; + +export const useGetReleaseStep = ({ + expectedStepKey, + expenditure, + expenditureStep, +}: ReleaseStepProps) => { + const { + setSelectedReleaseAction, + toggleOnReleaseModal: showReleasePaymentModal, + selectedReleaseAction, + } = usePaymentBuilderContext(); + + const { finalizingActions, finalizedAt, ownerAddress } = expenditure || {}; + + const sortedReleaseActions = useMemo( + () => + finalizingActions?.items.filter(notNull).sort((a, b) => { + if (a?.createdAt && b?.createdAt) { + return Date.parse(b.createdAt) - Date.parse(a.createdAt); + } + return 0; + }) ?? [], + [finalizingActions?.items], + ); + + const { motionData: selectedReleaseMotion } = selectedReleaseAction ?? {}; + const { + motionState: releaseMotionState, + refetchMotionState: refetchReleaseMotionState, + } = useGetColonyAction(selectedReleaseAction?.transactionHash); + + const allReleaseMotions = sortedReleaseActions + .map((releaseAction) => releaseAction.motionData) + .filter(notMaybe); + const isAnyReleaseMotionInProgress = allReleaseMotions.some( + (motion) => + !motion.isFinalized && !motion.motionStateHistory.hasFailedNotFinalizable, + ); + const shouldShowFundingMotionTimer = + releaseMotionState && + [ + MotionState.Staking, + MotionState.Supported, + MotionState.Voting, + MotionState.Reveal, + ].includes(releaseMotionState); + const shouldShowReleaseButton = !isAnyReleaseMotionInProgress; + useEffect(() => { + if (!selectedReleaseAction && sortedReleaseActions.length > 0) { + setSelectedReleaseAction(sortedReleaseActions[0]); + } + + if ( + selectedReleaseAction && + !sortedReleaseActions.some( + (releaseAction) => + releaseAction.transactionHash === + selectedReleaseAction.transactionHash, + ) + ) { + setSelectedReleaseAction(null); + } + }, [selectedReleaseAction, setSelectedReleaseAction, sortedReleaseActions]); + + return { + key: ExpenditureStep.Release, + heading: { + label: formatText({ id: 'expenditure.releaseStage.label' }), + decor: + selectedReleaseMotion && shouldShowFundingMotionTimer ? ( + + ) : null, + }, + content: ( +
+ {sortedReleaseActions.length > 0 && ( + + )} + + {selectedReleaseMotion && ( + + )} + + {selectedReleaseAction && !selectedReleaseMotion && ( + + )} + + {expenditureStep === ExpenditureStep.Release && + shouldShowReleaseButton ? ( + + + + } + /> + ) : ( + + ); +}; + +export default ReleaseRequestItem; diff --git a/src/components/v5/common/CompletedAction/partials/PaymentBuilder/partials/ReleaseRequests/ReleaseRequests.tsx b/src/components/v5/common/CompletedAction/partials/PaymentBuilder/partials/ReleaseRequests/ReleaseRequests.tsx new file mode 100644 index 0000000000..4db2478358 --- /dev/null +++ b/src/components/v5/common/CompletedAction/partials/PaymentBuilder/partials/ReleaseRequests/ReleaseRequests.tsx @@ -0,0 +1,37 @@ +import React, { type FC } from 'react'; +import { defineMessages } from 'react-intl'; + +import { formatText } from '~utils/intl.ts'; +import MenuContainer from '~v5/shared/MenuContainer/MenuContainer.tsx'; + +import ReleaseRequestItem from './ReleaseRequestItem.tsx'; +import { type ReleaseRequestsProps } from './types.ts'; + +const displayName = 'v5.common.CompletedAction.partials.ReleaseRequests'; + +const MSG = defineMessages({ + title: { + id: `${displayName}.title`, + defaultMessage: 'Release requests', + }, +}); + +const ReleaseRequests: FC = ({ actions }) => { + return ( + +
{formatText(MSG.title)}
+
    + {actions.map((action) => ( +
  • + +
  • + ))} +
+
+ ); +}; + +export default ReleaseRequests; diff --git a/src/components/v5/common/CompletedAction/partials/PaymentBuilder/partials/ReleaseRequests/types.ts b/src/components/v5/common/CompletedAction/partials/PaymentBuilder/partials/ReleaseRequests/types.ts new file mode 100644 index 0000000000..724bd62b04 --- /dev/null +++ b/src/components/v5/common/CompletedAction/partials/PaymentBuilder/partials/ReleaseRequests/types.ts @@ -0,0 +1,9 @@ +import { type ExpenditureAction } from '~types/graphql.ts'; + +export interface ReleaseRequestsProps { + actions: ExpenditureAction[]; +} + +export interface ReleaseRequestItemProps { + action: ExpenditureAction; +} diff --git a/src/context/PaymentBuilderContext/PaymentBuilderContext.ts b/src/context/PaymentBuilderContext/PaymentBuilderContext.ts index 9f06e1b0db..9e2428b722 100644 --- a/src/context/PaymentBuilderContext/PaymentBuilderContext.ts +++ b/src/context/PaymentBuilderContext/PaymentBuilderContext.ts @@ -12,6 +12,8 @@ export const PaymentBuilderContext = createContext<{ isReleaseModalOpen: boolean; selectedFundingAction: ExpenditureAction | null; setSelectedFundingAction: (action: ExpenditureAction | null) => void; + selectedReleaseAction: ExpenditureAction | null; + setSelectedReleaseAction: (action: ExpenditureAction | null) => void; }>({ toggleOnFundingModal: noop, toggleOffFundingModal: noop, @@ -21,6 +23,8 @@ export const PaymentBuilderContext = createContext<{ isReleaseModalOpen: false, selectedFundingAction: null, setSelectedFundingAction: noop, + selectedReleaseAction: null, + setSelectedReleaseAction: noop, }); export const usePaymentBuilderContext = () => useContext(PaymentBuilderContext); diff --git a/src/context/PaymentBuilderContext/PaymentBuilderContextProvider.tsx b/src/context/PaymentBuilderContext/PaymentBuilderContextProvider.tsx index 0787e8e5c9..84dc58d45a 100644 --- a/src/context/PaymentBuilderContext/PaymentBuilderContextProvider.tsx +++ b/src/context/PaymentBuilderContext/PaymentBuilderContextProvider.tsx @@ -21,6 +21,8 @@ const PaymentBuilderContextProvider: FC = ({ children }) => { ] = useToggle(); const [selectedFundingAction, setSelectedFundingAction] = useState(null); + const [selectedReleaseAction, setSelectedReleaseAction] = + useState(null); const value = useMemo( () => ({ @@ -32,6 +34,8 @@ const PaymentBuilderContextProvider: FC = ({ children }) => { isReleaseModalOpen, selectedFundingAction, setSelectedFundingAction, + selectedReleaseAction, + setSelectedReleaseAction, }), [ toggleOnFundingModal, @@ -41,6 +45,8 @@ const PaymentBuilderContextProvider: FC = ({ children }) => { toggleOffReleaseModal, isReleaseModalOpen, selectedFundingAction, + selectedReleaseAction, + setSelectedReleaseAction, ], ); diff --git a/src/graphql/fragments/expenditures.graphql b/src/graphql/fragments/expenditures.graphql index cad8a4cb08..65265ae3bf 100644 --- a/src/graphql/fragments/expenditures.graphql +++ b/src/graphql/fragments/expenditures.graphql @@ -62,7 +62,10 @@ fragment Expenditure on Expenditure { } finalizingActions: actions( filter: { - type: { eq: FINALIZE_EXPENDITURE } + or: [ + { type: { eq: FINALIZE_EXPENDITURE } } + { type: { eq: FINALIZE_EXPENDITURE_MOTION } } + ] isMotionFinalization: { ne: true } } ) { diff --git a/src/graphql/generated.ts b/src/graphql/generated.ts index 049a439fe1..886704170d 100644 --- a/src/graphql/generated.ts +++ b/src/graphql/generated.ts @@ -10766,7 +10766,7 @@ export const ExpenditureFragmentDoc = gql` } } finalizingActions: actions( - filter: {type: {eq: FINALIZE_EXPENDITURE}, isMotionFinalization: {ne: true}} + filter: {or: [{type: {eq: FINALIZE_EXPENDITURE}}, {type: {eq: FINALIZE_EXPENDITURE_MOTION}}], isMotionFinalization: {ne: true}} ) { items { ...ExpenditureAction diff --git a/src/i18n/en.json b/src/i18n/en.json index 80f4f0b1dd..ed98a52d1c 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -1762,6 +1762,7 @@ "decisionMethod.permissions": "Permissions", "decisionMethod.paymentCreator": "Payment creator", "decisionMethod.reputation": "Reputation", + "decisionMethod.paymentCreator": "Payment creator", "decisionMethod.staking": "Staking", "decisionMethodSelect.title": "Decision method", "decisionMethodSelect.placeholder": "Select method",