Skip to content

Commit

Permalink
Merge pull request #2309 from decentdao/fix/#2293-fix-active-payments…
Browse files Browse the repository at this point in the history
…-count

Proper active payments count `EVERYWHERE`
  • Loading branch information
Da-Colon authored Aug 28, 2024
2 parents f44a7cb + cddd759 commit 7e82a3d
Show file tree
Hide file tree
Showing 9 changed files with 62 additions and 54 deletions.
4 changes: 2 additions & 2 deletions src/components/pages/Roles/RolesTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ export function RolesRowEdit({
editStatus={editStatus}
/>
<MemberColumn wearerAddress={wearerAddress} />
<PaymentsColumn paymentsCount={payments?.length} />
<PaymentsColumn paymentsCount={payments?.filter(p => p.isStreaming()).length || undefined} />
</Tr>
);
}
Expand Down Expand Up @@ -265,7 +265,7 @@ export function RolesTable({
paymentsCount={
role.payments === undefined
? undefined
: role.payments.filter(p => p.isStreaming()).length
: role.payments.filter(p => p.isStreaming()).length || undefined
}
/>
))}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export function RoleFormPaymentStreams() {
asset: payment.asset,
endDate: payment.endDate,
startDate: payment.startDate,
cliffDate: payment.cliffDate,
isStreaming: () => false,
}}
onClick={() => {
Expand Down
32 changes: 27 additions & 5 deletions src/components/pages/Roles/forms/RoleFormTabs.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { Tab, TabList, TabPanels, TabPanel, Tabs, Button, Flex } from '@chakra-ui/react';
import { Button, Flex, Tab, TabList, TabPanel, TabPanels, Tabs } from '@chakra-ui/react';
import { useFormikContext } from 'formik';
import { useEffect, useRef } from 'react';
import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom';
import { Hex } from 'viem';
import { TOOLTIP_MAXW } from '../../../../constants/common';
import { DAO_ROUTES } from '../../../../constants/routes';
import { useFractal } from '../../../../providers/App/AppProvider';
import { useNetworkConfig } from '../../../../providers/NetworkConfig/NetworkConfigProvider';
import { useRolesStore } from '../../../../store/roles';
import ModalTooltip from '../../../ui/modals/ModalTooltip';
import { EditBadgeStatus, RoleFormValues, RoleHatFormValue } from '../types';
import RoleFormInfo from './RoleFormInfo';
import RoleFormPaymentStream from './RoleFormPaymentStream';
Expand All @@ -30,6 +32,7 @@ export default function RoleFormTabs({
const { editedRoleData, isRoleUpdated, existingRoleHat } = useRoleFormEditedRole({ hatsTree });
const { t } = useTranslation(['roles']);
const { values, errors, setFieldValue, setTouched } = useFormikContext<RoleFormValues>();
const paymentsTooltipRef = useRef<HTMLButtonElement>(null);

useEffect(() => {
if (values.hats.length && !values.roleEditing) {
Expand Down Expand Up @@ -64,15 +67,34 @@ export default function RoleFormTabs({
<Tabs variant="twoTone">
<TabList>
<Tab>{t('roleInfo')}</Tab>
<Tab>{t('payments')}</Tab>
<Tab
isDisabled={!hatsTree}
cursor={!hatsTree ? 'not-allowed' : 'pointer'}
ref={paymentsTooltipRef}
>
{!hatsTree ? (
<ModalTooltip
containerRef={paymentsTooltipRef}
label={t('tipPaymentsDisabled')}
placement="right"
maxW={TOOLTIP_MAXW}
>
{t('payments')}
</ModalTooltip>
) : (
t('payments')
)}
</Tab>
</TabList>
<TabPanels my="1.75rem">
<TabPanel>
<RoleFormInfo />
</TabPanel>
<TabPanel>
<RoleFormPaymentStreams />
</TabPanel>
{!!hatsTree && (
<TabPanel>
<RoleFormPaymentStreams />
</TabPanel>
)}
</TabPanels>
</Tabs>
<Flex
Expand Down
6 changes: 4 additions & 2 deletions src/components/pages/Roles/forms/useRoleFormEditedRole.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import { useMemo } from 'react';
import { DecentTree } from '../../../../store/roles';
import { EditedRole, EditBadgeStatus, RoleFormValues } from '../types';

const addRemoveField = (fieldNames: string[], fieldName: string, isRemoved: boolean) => {
if (fieldNames.includes(fieldName) && isRemoved) {
const addRemoveField = (fieldNames: string[], fieldName: string, hasChanges: boolean) => {
if (fieldNames.includes(fieldName) && !hasChanges) {
return fieldNames.filter(field => field !== fieldName);
} else if (!fieldNames.includes(fieldName) && !hasChanges) {
return fieldNames;
}
return [...fieldNames, fieldName];
};
Expand Down
21 changes: 9 additions & 12 deletions src/hooks/DAO/loaders/useHatsTree.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useApolloClient } from '@apollo/client';
import { Tree, HatsSubgraphClient } from '@hatsprotocol/sdk-v1-subgraph';
import { HatsSubgraphClient, Tree } from '@hatsprotocol/sdk-v1-subgraph';
import { useEffect } from 'react';
import { toast } from 'react-toastify';
import { formatUnits, getAddress } from 'viem';
Expand All @@ -8,7 +8,7 @@ import { StreamsQueryDocument } from '../../../../.graphclient';
import { SablierPayment } from '../../../components/pages/Roles/types';
import useIPFSClient from '../../../providers/App/hooks/useIPFSClient';
import { useNetworkConfig } from '../../../providers/NetworkConfig/NetworkConfigProvider';
import { useRolesStore, DecentHatsError } from '../../../store/roles';
import { DecentHatsError, useRolesStore } from '../../../store/roles';
import { CacheExpiry, CacheKeys } from '../../utils/cache/cacheDefaults';
import { getValue, setValue } from '../../utils/cache/useLocalStorage';

Expand Down Expand Up @@ -210,19 +210,16 @@ const useHatsTree = () => {
endDate,
cliffDate,
isStreaming: () => {
const start =
lockupLinearStream.cliff === undefined &&
lockupLinearStream.startTime !== undefined
? startDate.getTime()
: cliffDate !== undefined
? cliffDate.getTime()
: undefined;
const start = !lockupLinearStream.cliff
? startDate.getTime()
: cliffDate !== undefined
? cliffDate.getTime()
: undefined;
const end = endDate ? endDate.getTime() : undefined;
const cancelled = lockupLinearStream.canceled;
const now = new Date().getTime();
const isStreaming =
!cancelled && !!start && !!end && start <= now && end > now;
return isStreaming;

return !cancelled && !!start && !!end && start <= now && end > now;
},
};
},
Expand Down
13 changes: 5 additions & 8 deletions src/hooks/streams/useCreateSablierStream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,23 +56,20 @@ export default function useCreateSablierStream() {
throw new Error('Start date of the stream can not be larger than end date');
}

let cliffDuration = 0;
if (cliffDateTs) {
if (cliffDateTs <= startDateTs) {
throw new Error('Cliff date can not be less or equal than start date');
} else if (cliffDateTs >= endDateTs) {
throw new Error('Cliff date can not be larger or equal than end date');
}
cliffDuration = Math.ceil((cliffDateTs - startDateTs) / 1000);
}

const streamDuration = Math.ceil((endDateTs - startDateTs) / 1000);

return {
...prepareBasicStreamData(recipient, totalAmount),
durations: {
cliff: cliffDuration,
total: streamDuration + cliffDuration, // Total duration has to include cliff duration
timestamps: {
start: startDateTs,
end: endDateTs,
cliff: cliffDateTs,
},
};
},
Expand Down Expand Up @@ -126,7 +123,7 @@ export default function useCreateSablierStream() {
preparedStreamCreationTransactions.push({
calldata: encodeFunctionData({
abi: SablierV2BatchAbi,
functionName: 'createWithDurationsLL', // @dev @todo Another option would be to use `createWithTimestampsLL`. Probably makes sense to change the logic to `createWithTimestampsLL` since we drifted away from "durations" and actually operating with timestamps always
functionName: 'createWithTimestampsLL',
args: [sablierV2LockupLinear, tokenAddress, assembledStreams],
}),
targetAddress: sablierV2Batch,
Expand Down
36 changes: 12 additions & 24 deletions src/hooks/utils/useCreateRoles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ const identifyAndPrepareEditedPaymentStreams = (
return modifiedHats.flatMap(formHat => {
const currentHat = getHat(formHat.id);
if (currentHat === null) {
throw new Error("Couldn't find existing Hat for edited payment stream Hat.");
return [];
}

if (formHat.payments === undefined) {
Expand Down Expand Up @@ -211,9 +211,9 @@ const identifyAndPrepareEditedPaymentStreams = (
return {
streamId: payment.streamId,
recipient: currentHat.smartAddress,
startDateTs: payment.startDate.getTime(),
endDateTs: payment.endDate.getTime(),
cliffDateTs: payment.cliffDate?.getTime() ?? 0,
startDateTs: Math.floor(payment.startDate.getTime() / 1000),
endDateTs: Math.ceil(payment.endDate.getTime() / 1000),
cliffDateTs: Math.floor((payment.cliffDate?.getTime() ?? 0) / 1000),
totalAmount: payment.amount.bigintValue,
assetAddress: payment.asset.address,
roleHatId: BigInt(currentHat.id),
Expand All @@ -232,7 +232,7 @@ const identifyAndPrepareAddedPaymentStreams = async (
): Promise<PreparedNewStreamData[]> => {
const preparedStreamDataMapped = await Promise.all(
modifiedHats.map(async formHat => {
if (formHat.payments === undefined || formHat.editedRole.status !== EditBadgeStatus.Updated) {
if (formHat.payments === undefined) {
return [];
}

Expand Down Expand Up @@ -263,9 +263,9 @@ const identifyAndPrepareAddedPaymentStreams = async (

return {
recipient: recipientAddress,
startDateTs: payment.startDate.getTime(),
endDateTs: payment.endDate.getTime(),
cliffDateTs: payment.cliffDate?.getTime() ?? 0,
startDateTs: Math.floor(payment.startDate.getTime() / 1000),
endDateTs: Math.ceil(payment.endDate.getTime() / 1000),
cliffDateTs: Math.floor((payment.cliffDate?.getTime() ?? 0) / 1000),
totalAmount: payment.amount.bigintValue,
assetAddress: payment.asset.address,
};
Expand Down Expand Up @@ -663,14 +663,8 @@ export default function useCreateRoles() {

if (addedPaymentStreams.length) {
const preparedPaymentTransactions = prepareBatchLinearStreamCreation(addedPaymentStreams);
addedPaymentStreams.forEach((_, i) => {
hatPaymentAddedTxs.push(
preparedPaymentTransactions.preparedTokenApprovalsTransactions[i],
);
hatPaymentAddedTxs.push(
preparedPaymentTransactions.preparedStreamCreationTransactions[i],
);
});
hatPaymentAddedTxs.push(...preparedPaymentTransactions.preparedTokenApprovalsTransactions);
hatPaymentAddedTxs.push(...preparedPaymentTransactions.preparedStreamCreationTransactions);
}

if (editedPaymentStreams.length) {
Expand Down Expand Up @@ -733,14 +727,8 @@ export default function useCreateRoles() {

const preparedPaymentTransactions = prepareBatchLinearStreamCreation(editedPaymentStreams);
hatPaymentEditedTxs.push(...paymentCancelTxs);
editedPaymentStreams.forEach((_, i) => {
hatPaymentEditedTxs.push(
preparedPaymentTransactions.preparedTokenApprovalsTransactions[i],
);
hatPaymentEditedTxs.push(
preparedPaymentTransactions.preparedStreamCreationTransactions[i],
);
});
hatPaymentEditedTxs.push(...preparedPaymentTransactions.preparedTokenApprovalsTransactions);
hatPaymentEditedTxs.push(...preparedPaymentTransactions.preparedStreamCreationTransactions);
}

const proposalTransactions = {
Expand Down
1 change: 1 addition & 0 deletions src/i18n/locales/en/roles.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
"activePayments": "Active Payments",
"payments": "Payments",
"payment": "Payment",
"tipPaymentsDisabled": "Creating payments can be accessed after initializing roles. You need to create at least 1 role in order to create payments for it or for other roles.",
"withdraw": "Withdraw",
"available": "Available",
"withdrawPendingMessage": "Withdrawing your payment, hang tight",
Expand Down
2 changes: 1 addition & 1 deletion src/pages/daos/[daoAddress]/roles/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ function Roles() {
wearerAddress={roleHat.wearer || zeroAddress}
hatId={roleHat.id}
handleRoleClick={handleNavigateToRole}
paymentsCount={roleHat.payments?.length}
paymentsCount={roleHat.payments?.filter(p => p.isStreaming()).length || undefined}
/>
))}
</Show>
Expand Down

0 comments on commit 7e82a3d

Please sign in to comment.