Skip to content

Commit

Permalink
feat: added reputation method to release
Browse files Browse the repository at this point in the history
  • Loading branch information
CzarekDryl committed Oct 1, 2024
1 parent 5b0f599 commit 0324712
Show file tree
Hide file tree
Showing 14 changed files with 347 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,14 @@ 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';
import PaymentStepDetailsBlock from '../PaymentStepDetailsBlock/PaymentStepDetailsBlock.tsx';
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,
Expand All @@ -56,7 +56,6 @@ const PaymentBuilderWidget: FC<PaymentBuilderWidgetProps> = ({ action }) => {
toggleOnFundingModal: showFundingModal,
isReleaseModalOpen: isReleasePaymentModalOpen,
toggleOffReleaseModal: hideReleasePaymentModal,
toggleOnReleaseModal: showReleasePaymentModal,
selectedFundingAction,
setSelectedFundingAction,
} = usePaymentBuilderContext();
Expand All @@ -66,15 +65,8 @@ const PaymentBuilderWidget: FC<PaymentBuilderWidgetProps> = ({ 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);
Expand Down Expand Up @@ -107,6 +99,12 @@ const PaymentBuilderWidget: FC<PaymentBuilderWidgetProps> = ({ action }) => {
}
}, [expectedStepKey, expenditureStep]);

const releaseStep = useGetReleaseStep({
expectedStepKey,
expenditure,
expenditureStep,
});

const lockExpenditurePayload: LockExpenditurePayload | null = useMemo(
() =>
expenditure
Expand Down Expand Up @@ -302,47 +300,7 @@ const PaymentBuilderWidget: FC<PaymentBuilderWidgetProps> = ({ action }) => {
</div>
),
},
{
key: ExpenditureStep.Release,
heading: { label: formatText({ id: 'expenditure.releaseStage.label' }) },
content:
expenditureStep === ExpenditureStep.Release ? (
<StepDetailsBlock
text={formatText({
id: 'expenditure.releaseStage.info',
})}
content={
<Button
className="w-full"
onClick={showReleasePaymentModal}
text={formatText({
id: 'expenditure.releaseStage.button',
})}
loading={expectedStepKey === ExpenditureStep.Payment}
/>
}
/>
) : (
<>
{finalizedAt ? (
<>
{finalizingActions?.items[0]?.initiatorAddress ===
ownerAddress ? (
<FinalizeByPaymentCreatorInfo
userAdddress={expenditure?.ownerAddress}
/>
) : (
<ActionWithPermissionsInfo
action={finalizingActions?.items[0]}
/>
)}
</>
) : (
<div />
)}
</>
),
},
releaseStep,
{
key: ExpenditureStep.Payment,
heading: { label: formatText({ id: 'expenditure.paymentStage.label' }) },
Expand Down
Original file line number Diff line number Diff line change
@@ -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 ? (
<MotionCountDownTimer
motionState={releaseMotionState}
motionId={selectedReleaseMotion.motionId}
motionStakes={selectedReleaseMotion.motionStakes}
refetchMotionState={refetchReleaseMotionState}
/>
) : null,
},
content: (
<div className="flex flex-col gap-2">
{sortedReleaseActions.length > 0 && (
<ReleaseRequests actions={sortedReleaseActions} />
)}

{selectedReleaseMotion && (
<MotionBox transactionId={selectedReleaseMotion.transactionHash} />
)}

{selectedReleaseAction && !selectedReleaseMotion && (
<ActionWithPermissionsInfo action={selectedReleaseAction} />
)}

{expenditureStep === ExpenditureStep.Release &&
shouldShowReleaseButton ? (
<StepDetailsBlock
text={formatText({
id: 'expenditure.releaseStage.info',
})}
content={
expectedStepKey === ExpenditureStep.Payment ? (
<IconButton
className="max-h-[2.5rem] w-full !text-md"
rounded="s"
text={{ id: 'button.pending' }}
icon={
<span className="ml-1.5 flex shrink-0">
<SpinnerGap className="animate-spin" size={14} />
</span>
}
/>
) : (
<Button
className="w-full"
onClick={showReleasePaymentModal}
text={formatText({
id: 'expenditure.releaseStage.button',
})}
/>
)
}
/>
) : (
<>
{finalizedAt ? (
<>
{finalizingActions?.items[0]?.initiatorAddress ===
ownerAddress ? (
<FinalizeByPaymentCreatorInfo
userAdddress={expenditure?.ownerAddress}
/>
) : (
<ActionWithPermissionsInfo
action={finalizingActions?.items[0]}
/>
)}
</>
) : (
<div />
)}
</>
)}
</div>
),
};
};
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type ColonyAction } from '~types/graphql.ts';
import { type Expenditure, type ColonyAction } from '~types/graphql.ts';

export enum ExpenditureStep {
Create = 'CREATE',
Expand All @@ -12,3 +12,9 @@ export enum ExpenditureStep {
export interface PaymentBuilderWidgetProps {
action: ColonyAction;
}

export interface ReleaseStepProps {
expenditure: Expenditure | undefined | null;
expectedStepKey: ExpenditureStep | null;
expenditureStep: ExpenditureStep | null;
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import { Id } from '@colony/colony-js';
import { SpinnerGap, Wallet } from '@phosphor-icons/react';
import React, { type FC } from 'react';

import { useAppContext } from '~context/AppContext/AppContext.ts';
import { useColonyContext } from '~context/ColonyContext/ColonyContext.ts';
import useAsyncFunction from '~hooks/useAsyncFunction.ts';
import useEnabledExtensions from '~hooks/useEnabledExtensions.ts';
import { ActionTypes } from '~redux';
import { type FinalizeExpenditurePayload } from '~redux/sagas/expenditures/finalizeExpenditure.ts';
import { type ReclaimExpenditureStakePayload } from '~redux/sagas/expenditures/reclaimExpenditureStake.ts';
import { type FinalizeExpenditureMotionPayload } from '~redux/sagas/motions/expenditures/finalizeExpenditureMotion.ts';
import { Form } from '~shared/Fields/index.ts';
import { DecisionMethod } from '~types/actions.ts';
import { getClaimableExpenditurePayouts } from '~utils/expenditures.ts';
import { formatText } from '~utils/intl.ts';
import Button from '~v5/shared/Button/Button.tsx';
Expand Down Expand Up @@ -42,6 +46,12 @@ const ReleasePaymentModal: FC<ReleasePaymentModalProps> = ({
({ isDisabled }) => isDisabled,
);

const finalizeExpenditureViaMotion = useAsyncFunction({
submit: ActionTypes.MOTION_EXPENDITURE_FINALIZE,
error: ActionTypes.MOTION_EXPENDITURE_FINALIZE_ERROR,
success: ActionTypes.MOTION_EXPENDITURE_FINALIZE_SUCCESS,
});

const finalizeExpenditure = useAsyncFunction({
submit: ActionTypes.EXPENDITURE_FINALIZE,
error: ActionTypes.EXPENDITURE_FINALIZE_ERROR,
Expand All @@ -54,7 +64,9 @@ const ReleasePaymentModal: FC<ReleasePaymentModalProps> = ({
success: ActionTypes.RECLAIM_EXPENDITURE_STAKE_SUCCESS,
});

const handleFinalizeExpenditure = async () => {
const { votingReputationAddress } = useEnabledExtensions();

const handleFinalizeExpenditure = async ({ decisionMethod }) => {
try {
if (!expenditure) {
return;
Expand All @@ -66,7 +78,21 @@ const ReleasePaymentModal: FC<ReleasePaymentModalProps> = ({
userAddress: user?.walletAddress ?? '',
};

await finalizeExpenditure(finalizePayload);
const motionFinalizepayload: FinalizeExpenditureMotionPayload = {
colony,
expenditure,
votingReputationAddress: votingReputationAddress || '',
motionDomainId: Id.RootDomain,
};

if (
decisionMethod &&
decisionMethod.value === DecisionMethod.Reputation
) {
await finalizeExpenditureViaMotion(motionFinalizepayload);
} else {
await finalizeExpenditure(finalizePayload);
}

const claimablePayouts = getClaimableExpenditurePayouts(
expenditure.slots,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ export const releaseDecisionMethodDescriptions: Partial<
[DecisionMethod.Permissions]: formatText({
id: 'releaseModal.permissionsDescription',
}),
[DecisionMethod.Reputation]: formatText({
id: 'fundingModal.reputationDescription',
}),
[DecisionMethod.PaymentCreator]: formatText({
id: 'releaseModal.paymentCreatorDescription',
}),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { type Action } from '~constants/actions.ts';
import { useAppContext } from '~context/AppContext/AppContext.ts';
import useEnabledExtensions from '~hooks/useEnabledExtensions.ts';
import { DecisionMethod } from '~types/actions.ts';
import { type Expenditure } from '~types/graphql.ts';
import { formatText } from '~utils/intl.ts';
Expand All @@ -13,6 +14,7 @@ export const useGetReleaseDecisionMethodItems = (
): DecisionMethodOption[] => {
const { user } = useAppContext();
const isPermissionsEnabled = useCheckIfUserHasPermissions(actionType);
const { isVotingReputationEnabled } = useEnabledExtensions();

const userIsCreator = user?.walletAddress === expenditure.ownerAddress;

Expand All @@ -30,6 +32,16 @@ export const useGetReleaseDecisionMethodItems = (
},
]
: []),
...(isVotingReputationEnabled
? [
{
label: formatText({
id: 'decisionMethodSelect.decision.reputation',
}),
value: DecisionMethod.Reputation,
},
]
: []),
...(userIsCreator
? [
{
Expand Down
Loading

0 comments on commit 0324712

Please sign in to comment.