diff --git a/src/components/pages/Roles/RolesTable.tsx b/src/components/pages/Roles/RolesTable.tsx
index d73f535e18..6a6b8a60c0 100644
--- a/src/components/pages/Roles/RolesTable.tsx
+++ b/src/components/pages/Roles/RolesTable.tsx
@@ -221,7 +221,7 @@ export function RolesRowEdit({
editStatus={editStatus}
/>
-
+ p.isStreaming()).length || undefined} />
);
}
@@ -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
}
/>
))}
diff --git a/src/components/pages/Roles/forms/RoleFormPaymentStreams.tsx b/src/components/pages/Roles/forms/RoleFormPaymentStreams.tsx
index 7e03a8fa0c..797ea29a16 100644
--- a/src/components/pages/Roles/forms/RoleFormPaymentStreams.tsx
+++ b/src/components/pages/Roles/forms/RoleFormPaymentStreams.tsx
@@ -41,6 +41,7 @@ export function RoleFormPaymentStreams() {
asset: payment.asset,
endDate: payment.endDate,
startDate: payment.startDate,
+ cliffDate: payment.cliffDate,
isStreaming: () => false,
}}
onClick={() => {
diff --git a/src/components/pages/Roles/forms/RoleFormTabs.tsx b/src/components/pages/Roles/forms/RoleFormTabs.tsx
index a2d60d148b..d037cbfddb 100644
--- a/src/components/pages/Roles/forms/RoleFormTabs.tsx
+++ b/src/components/pages/Roles/forms/RoleFormTabs.tsx
@@ -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';
@@ -30,6 +32,7 @@ export default function RoleFormTabs({
const { editedRoleData, isRoleUpdated, existingRoleHat } = useRoleFormEditedRole({ hatsTree });
const { t } = useTranslation(['roles']);
const { values, errors, setFieldValue, setTouched } = useFormikContext();
+ const paymentsTooltipRef = useRef(null);
useEffect(() => {
if (values.hats.length && !values.roleEditing) {
@@ -64,15 +67,34 @@ export default function RoleFormTabs({
{t('roleInfo')}
- {t('payments')}
+
+ {!hatsTree ? (
+
+ {t('payments')}
+
+ ) : (
+ t('payments')
+ )}
+
-
-
-
+ {!!hatsTree && (
+
+
+
+ )}
{
- 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];
};
diff --git a/src/hooks/DAO/loaders/useHatsTree.ts b/src/hooks/DAO/loaders/useHatsTree.ts
index 943cd7cbf9..22a24250ef 100644
--- a/src/hooks/DAO/loaders/useHatsTree.ts
+++ b/src/hooks/DAO/loaders/useHatsTree.ts
@@ -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';
@@ -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';
@@ -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;
},
};
},
diff --git a/src/hooks/streams/useCreateSablierStream.ts b/src/hooks/streams/useCreateSablierStream.ts
index e4d8d51d97..0af300e021 100644
--- a/src/hooks/streams/useCreateSablierStream.ts
+++ b/src/hooks/streams/useCreateSablierStream.ts
@@ -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,
},
};
},
@@ -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,
diff --git a/src/hooks/utils/useCreateRoles.ts b/src/hooks/utils/useCreateRoles.ts
index e44da0bd4f..c938755a2c 100644
--- a/src/hooks/utils/useCreateRoles.ts
+++ b/src/hooks/utils/useCreateRoles.ts
@@ -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) {
@@ -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),
@@ -232,7 +232,7 @@ const identifyAndPrepareAddedPaymentStreams = async (
): Promise => {
const preparedStreamDataMapped = await Promise.all(
modifiedHats.map(async formHat => {
- if (formHat.payments === undefined || formHat.editedRole.status !== EditBadgeStatus.Updated) {
+ if (formHat.payments === undefined) {
return [];
}
@@ -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,
};
@@ -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) {
@@ -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 = {
diff --git a/src/i18n/locales/en/roles.json b/src/i18n/locales/en/roles.json
index 846fd01b38..fdee444b58 100644
--- a/src/i18n/locales/en/roles.json
+++ b/src/i18n/locales/en/roles.json
@@ -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",
diff --git a/src/pages/daos/[daoAddress]/roles/index.tsx b/src/pages/daos/[daoAddress]/roles/index.tsx
index 72c54ea5ca..32028de4ed 100644
--- a/src/pages/daos/[daoAddress]/roles/index.tsx
+++ b/src/pages/daos/[daoAddress]/roles/index.tsx
@@ -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}
/>
))}