diff --git a/src/locales/messages.pot b/src/locales/messages.pot index 08f3ebe39c..a6d5c40ccf 100644 --- a/src/locales/messages.pot +++ b/src/locales/messages.pot @@ -92,6 +92,9 @@ msgstr "" msgid "Claim {tokensLabel} as ERC-20" msgstr "" +msgid "Save project" +msgstr "" + msgid "Total issuance" msgstr "" @@ -2807,6 +2810,9 @@ msgstr "" msgid "We've disabled payments because the project has opted to reserve 100% of new tokens. You would receive no tokens from your payment." msgstr "" +msgid "Get notifications" +msgstr "" + msgid "Unarchiving your project has the following effects:" msgstr "" diff --git a/src/packages/v1/components/V1Project/V1ProjectToolsDrawer/V1ProjectToolsDrawer.tsx b/src/packages/v1/components/V1Project/V1ProjectToolsDrawer/V1ProjectToolsDrawer.tsx index dc342a7ff0..884f91356f 100644 --- a/src/packages/v1/components/V1Project/V1ProjectToolsDrawer/V1ProjectToolsDrawer.tsx +++ b/src/packages/v1/components/V1Project/V1ProjectToolsDrawer/V1ProjectToolsDrawer.tsx @@ -1,13 +1,13 @@ import { Trans } from '@lingui/macro' import { Divider, Drawer, Space, Tabs } from 'antd' -import { AddToProjectBalanceForm } from 'components/Project/ProjectToolsDrawer/AddToProjectBalanceForm' -import { ExportSection } from 'components/Project/ProjectToolsDrawer/ExportSection' -import { TransferOwnershipForm } from 'components/Project/ProjectToolsDrawer/TransferOwnershipForm' import { useIsUserAddress } from 'hooks/useIsUserAddress' import { V1ProjectContext } from 'packages/v1/contexts/Project/V1ProjectContext' import { useAddToBalanceTx } from 'packages/v1/hooks/transactor/useAddToBalanceTx' import { useSafeTransferFromTx } from 'packages/v1/hooks/transactor/useSafeTransferFromTx' import { useSetProjectUriTx } from 'packages/v1/hooks/transactor/useSetProjectUriTx' +import { AddToProjectBalanceForm } from 'packages/v2v3/components/V2V3Project/V2V3ProjectToolsDrawer/AddToProjectBalanceForm' +import { ExportSection } from 'packages/v2v3/components/V2V3Project/V2V3ProjectToolsDrawer/ExportSection' +import { TransferOwnershipForm } from 'packages/v2v3/components/V2V3Project/V2V3ProjectToolsDrawer/TransferOwnershipForm' import { useContext } from 'react' import ArchiveV1Project from './ArchiveV1Project' import { ExportPayoutModsButton } from './ExportPayoutModsButton' diff --git a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/ProjectHeaderPopupMenu.tsx b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/ProjectHeaderPopupMenu.tsx new file mode 100644 index 0000000000..1e4e1a6d5c --- /dev/null +++ b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/ProjectHeaderPopupMenu.tsx @@ -0,0 +1,120 @@ +import { WrenchScrewdriverIcon } from '@heroicons/react/24/outline' +import { Trans } from '@lingui/macro' +import { SocialLinkButton } from 'components/Project/ProjectHeader/SocialLinkButton' +import { useSocialLinks } from 'components/Project/ProjectHeader/hooks/useSocialLinks' +import { BookmarkButtonIcon } from 'components/buttons/BookmarkButton/BookmarkButtonIcon' +import { useBookmarkButton } from 'components/buttons/BookmarkButton/hooks/useBookmarkButton' +import { SubscribeButtonIcon } from 'components/buttons/SubscribeButton/SubscribeButtonIcon' +import { useSubscribeButton } from 'components/buttons/SubscribeButton/hooks/useSubscribeButton' +import { PopupMenu } from 'components/ui/PopupMenu' +import { PV_V2 } from 'constants/pv' +import useMobile from 'hooks/useMobile' +import { useMemo, useState } from 'react' +import { twJoin } from 'tailwind-merge' +import { V2V3ProjectToolsDrawer } from '../../V2V3ProjectToolsDrawer' + +type SocialLink = 'twitter' | 'discord' | 'telegram' | 'website' + +export function ProjectHeaderPopupMenu({ + className, + projectId, +}: { + className?: string + projectId: number +}) { + const socialLinks = useSocialLinks() + const isMobile = useMobile() + const [toolsIsOpen, setToolsIsOpen] = useState() + + const { isBookmarked, onBookmarkButtonClicked } = useBookmarkButton({ + projectId, + pv: PV_V2, + }) + const { isSubscribed, onSubscribeButtonClicked } = useSubscribeButton({ + projectId, + }) + + const socialItems = useMemo( + () => Object.entries(socialLinks).filter(([, href]) => !!href), + [socialLinks], + ) as [string, string][] + + return ( + <> + ({ + id: type, + label: ( + + ), + href, + })) + : []), + { + id: 'subscribe', + label: ( + <> + + + + Get notifications + + + ), + onClick: onSubscribeButtonClicked, + }, + { + id: 'bookmark', + label: ( + <> + + + Save project + + + ), + onClick(ev) { + ev.preventDefault() + ev.stopPropagation() + + onBookmarkButtonClicked() + }, + }, + { + id: 'tools', + label: ( + <> + + + + Tools + + + ), + onClick: ev => { + ev.preventDefault() + ev.stopPropagation() + + setToolsIsOpen(true) + }, + }, + ]} + /> + + setToolsIsOpen(false)} + /> + + ) +} diff --git a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/V2V3ProjectHeader.tsx b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/V2V3ProjectHeader.tsx index dca6c7b878..ddcff0e240 100644 --- a/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/V2V3ProjectHeader.tsx +++ b/src/packages/v2v3/components/V2V3Project/ProjectDashboard/components/V2V3ProjectHeader.tsx @@ -6,7 +6,6 @@ import { DomainBadge } from 'components/DomainBadge' import EthereumAddress from 'components/EthereumAddress' import { GnosisSafeBadge } from 'components/Project/ProjectHeader/GnosisSafeBadge' import { ProjectHeaderLogo } from 'components/Project/ProjectHeader/ProjectHeaderLogo' -import { ProjectHeaderPopupMenu } from 'components/Project/ProjectHeader/ProjectHeaderPopupMenu' import { SocialLinkButton } from 'components/Project/ProjectHeader/SocialLinkButton' import { Subtitle } from 'components/Project/ProjectHeader/Subtitle' import { useSocialLinks } from 'components/Project/ProjectHeader/hooks/useSocialLinks' @@ -21,6 +20,7 @@ import { V2V3OperatorPermission } from 'packages/v2v3/models/v2v3Permissions' import { settingsPagePath, v2v3ProjectRoute } from 'packages/v2v3/utils/routes' import { twMerge } from 'tailwind-merge' import { SocialLink } from '../hooks/useAboutPanel' +import { ProjectHeaderPopupMenu } from './ProjectHeaderPopupMenu' export const V2V3ProjectHeader = ({ className }: { className?: string }) => { const socialLinks = useSocialLinks() diff --git a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/TokensSection/MintRateField.tsx b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/TokensSection/MintRateField.tsx index d9aaddb0e7..9b708bba3f 100644 --- a/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/TokensSection/MintRateField.tsx +++ b/src/packages/v2v3/components/V2V3Project/V2V3ProjectSettings/pages/EditCyclePage/TokensSection/MintRateField.tsx @@ -3,10 +3,9 @@ import { Form } from 'antd' import FormattedNumberInput from 'components/inputs/FormattedNumberInput' import { MAX_MINT_RATE } from 'packages/v2v3/utils/math' -// Note: "issuanceRate" = "mintRate" export function MintRateField() { return ( - + () - const { isBookmarked, onBookmarkButtonClicked } = useBookmarkButton({ - projectId, - pv: PV_V2, - }) - const { isSubscribed, onSubscribeButtonClicked } = useSubscribeButton({ - projectId, - }) + // const { isBookmarked, onBookmarkButtonClicked } = useBookmarkButton({ + // projectId, + // pv: PV_V2, + // }) + // const { isSubscribed, onSubscribeButtonClicked } = useSubscribeButton({ + // projectId, + // }) const socialItems = useMemo( () => Object.entries(socialLinks).filter(([, href]) => !!href), @@ -109,7 +106,7 @@ export function ProjectHeaderPopupMenu({ ]} /> - setToolsIsOpen(false)} /> diff --git a/src/packages/v4/components/ProjectDashboard/components/V4ProjectToolsDrawer/AddToProjectBalanceForm.tsx b/src/packages/v4/components/ProjectDashboard/components/V4ProjectToolsDrawer/AddToProjectBalanceForm.tsx new file mode 100644 index 0000000000..71fd4a421e --- /dev/null +++ b/src/packages/v4/components/ProjectDashboard/components/V4ProjectToolsDrawer/AddToProjectBalanceForm.tsx @@ -0,0 +1,100 @@ +import { Trans } from '@lingui/macro' +import { waitForTransactionReceipt } from '@wagmi/core' +import { Form } from 'antd' +import InputAccessoryButton from 'components/buttons/InputAccessoryButton' +import TransactorButton from 'components/buttons/TransactorButton' +import FormattedNumberInput from 'components/inputs/FormattedNumberInput' +import { TxHistoryContext } from 'contexts/Transaction/TxHistoryContext' +import { useWallet } from 'hooks/Wallet' +import { NATIVE_TOKEN } from 'juice-sdk-core' +import { useJBContractContext, useWriteJbMultiTerminalAddToBalanceOf } from 'juice-sdk-react' +import { wagmiConfig } from 'packages/v4/wagmiConfig' +import { useContext, useState } from 'react' +import { parseWad } from 'utils/format/formatNumber' +import { emitErrorNotification } from 'utils/notifications' +import { reloadWindow } from 'utils/windowUtils' + +export function AddToProjectBalanceForm() { + const { contracts, projectId } = useJBContractContext() + const { addTransaction } = useContext(TxHistoryContext) + + const [loadingAddToBalance, setLoadingAddToBalance] = useState() + + const { userAddress } = useWallet() + + const [addToBalanceForm] = Form.useForm<{ amount: string }>() + + const { writeContractAsync: writeAddToBalance } = + useWriteJbMultiTerminalAddToBalanceOf() + + + async function addToBalance() { + const amount = parseWad(addToBalanceForm.getFieldValue('amount')).toBigInt() + if ( + !amount || + !contracts.primaryNativeTerminal.data || + !projectId + ) + return + + setLoadingAddToBalance(true) + + const args = [ + projectId, + NATIVE_TOKEN, + amount, + false, // shouldReturnHeldFees + '', // memo + '0x', // metadata + ] as const + + try { + const hash = await writeAddToBalance({ + address: contracts.primaryNativeTerminal.data, + args, + }) + + addTransaction?.('Send payouts', { hash }) + await waitForTransactionReceipt(wagmiConfig, { + hash, + }) + + reloadWindow() + + setLoadingAddToBalance(false) + } catch (e) { + setLoadingAddToBalance(false) + + emitErrorNotification((e as unknown as Error).message) + } + } + + return ( +
+

+ Transfer ETH to this project +

+

+ + Transfer ETH from your wallet to this project without minting tokens. + +

+ + Transfer amount}> + } + /> + + addToBalance()} + loading={loadingAddToBalance} + size="small" + type="primary" + text={Transfer ETH to project} + disabled={!userAddress} + connectWalletText={Connect wallet to transfer ETH} + /> + + ) +} diff --git a/src/packages/v4/components/ProjectDashboard/components/V4ProjectToolsDrawer/ExportSplitsButton.tsx b/src/packages/v4/components/ProjectDashboard/components/V4ProjectToolsDrawer/ExportSplitsButton.tsx new file mode 100644 index 0000000000..6f67f1fca1 --- /dev/null +++ b/src/packages/v4/components/ProjectDashboard/components/V4ProjectToolsDrawer/ExportSplitsButton.tsx @@ -0,0 +1,104 @@ +import { DownloadOutlined } from '@ant-design/icons' +import { t } from '@lingui/macro' +import { Button } from 'antd' +import { ETH_PAYOUT_SPLIT_GROUP } from 'constants/splits' +import { ProjectMetadataContext } from 'contexts/ProjectMetadataContext' +import { BigNumber } from 'ethers' +import { V2V3ProjectContext } from 'packages/v2v3/contexts/Project/V2V3ProjectContext' +import { GroupedSplits, Split, SplitGroup } from 'packages/v2v3/models/splits' +import { formatSplitPercent } from 'packages/v2v3/utils/math' +import { getProjectOwnerRemainderSplit } from 'packages/v2v3/utils/v2v3Splits' +import { PropsWithChildren, useContext, useState } from 'react' +import { downloadCsvFile } from 'utils/csv' +import { emitErrorNotification } from 'utils/notifications' + +const CSV_HEADER = [ + 'beneficiary', + 'percent', + 'preferClaimed', + 'lockedUntil', + 'projectId', + 'allocator', +] + +const splitToCsvRow = (split: Split) => { + return [ + split.beneficiary, + `${parseFloat(formatSplitPercent(BigNumber.from(split.percent))) / 100}`, + `${split.preferClaimed}`, + `${split.lockedUntil}`, + split.projectId, + split.allocator, + ] +} + +const prepareSplitsCsv = ( + splits: Split[], + projectOwnerAddress: string, +): (string | undefined)[][] => { + const csvContent = splits.map(splitToCsvRow) + + const rows = [CSV_HEADER, ...csvContent] + + const projectOwnerSplit = getProjectOwnerRemainderSplit( + projectOwnerAddress, + splits, + ) + if (projectOwnerSplit.percent > 0) { + rows.push(splitToCsvRow(projectOwnerSplit)) + } + + return rows +} + +export function ExportSplitsButton({ + children, + groupedSplits, +}: PropsWithChildren<{ groupedSplits: GroupedSplits }>) { + const { handle, fundingCycle, projectOwnerAddress } = + useContext(V2V3ProjectContext) + const { projectId } = useContext(ProjectMetadataContext) + const [loading, setLoading] = useState(false) + + const onExportSplitsButtonClick = () => { + if (!groupedSplits || !fundingCycle || !projectOwnerAddress) { + emitErrorNotification( + t`CSV data wasn't ready for export. Wait a few seconds and try again.`, + ) + return + } + + setLoading(true) + + try { + const csvContent = prepareSplitsCsv( + groupedSplits.splits, + projectOwnerAddress, + ) + const projectIdentifier = handle ? `@${handle}` : `project-${projectId}` + const splitType = + groupedSplits.group === ETH_PAYOUT_SPLIT_GROUP + ? 'payouts' + : 'reserved-tokens' + const filename = `${projectIdentifier}_${splitType}_fc-${fundingCycle.number}` + + downloadCsvFile(filename, csvContent) + } catch (e) { + console.error(e) + emitErrorNotification(t`CSV download failed.`) + } finally { + setLoading(false) + } + } + + return ( + + ) +} diff --git a/src/packages/v4/components/ProjectDashboard/components/V4ProjectToolsDrawer/PaymentAddressSection/LaunchProjectPayerButton.tsx b/src/packages/v4/components/ProjectDashboard/components/V4ProjectToolsDrawer/PaymentAddressSection/LaunchProjectPayerButton.tsx new file mode 100644 index 0000000000..5b8b43f584 --- /dev/null +++ b/src/packages/v4/components/ProjectDashboard/components/V4ProjectToolsDrawer/PaymentAddressSection/LaunchProjectPayerButton.tsx @@ -0,0 +1,23 @@ +import { Trans } from '@lingui/macro' +import { Button } from 'antd' +import { useState } from 'react' +import { LaunchProjectPayerModal } from './LaunchProjectPayerModal/LaunchProjectPayerModal' + +export function LaunchProjectPayerButton() { + const [modalVisible, setModalVisible] = useState(false) + + return ( + <> + + + setModalVisible(false)} + /> + + ) +} diff --git a/src/packages/v4/components/ProjectDashboard/components/V4ProjectToolsDrawer/PaymentAddressSection/LaunchProjectPayerModal/AdvancedOptionsCollapse.tsx b/src/packages/v4/components/ProjectDashboard/components/V4ProjectToolsDrawer/PaymentAddressSection/LaunchProjectPayerModal/AdvancedOptionsCollapse.tsx new file mode 100644 index 0000000000..232e6a9ae0 --- /dev/null +++ b/src/packages/v4/components/ProjectDashboard/components/V4ProjectToolsDrawer/PaymentAddressSection/LaunchProjectPayerModal/AdvancedOptionsCollapse.tsx @@ -0,0 +1,132 @@ +import { t, Trans } from '@lingui/macro' +import { Form, FormInstance, Input, Switch } from 'antd' +import { EthAddressInput } from 'components/inputs/EthAddressInput' +import { FormImageUploader } from 'components/inputs/FormImageUploader' +import { MinimalCollapse } from 'components/MinimalCollapse' +import TooltipLabel from 'components/TooltipLabel' +import { V2V3ProjectContext } from 'packages/v2v3/contexts/Project/V2V3ProjectContext' +import { useContext, useState } from 'react' +import { isZeroAddress } from 'utils/address' + +import type { AdvancedOptionsFields } from './LaunchProjectPayerModal' + +const defaultAdvancedOptions: AdvancedOptionsFields = { + memo: '', + memoImageUrl: undefined, + tokenMintingEnabled: true, + preferClaimed: false, + customBeneficiaryAddress: undefined, +} + +export default function AdvancedOptionsCollapse({ + form, +}: { + form: FormInstance +}) { + const { tokenAddress } = useContext(V2V3ProjectContext) + + // need state for this field to update dom + const [tokenMintingEnabled, setTokenMintingEnabled] = useState( + form.getFieldValue('tokenMintingEnabled') === false ? false : true, + ) + + const [customBeneficiaryEnabled, setCustomBeneficiaryEnabled] = + useState(Boolean(form.getFieldValue('customBeneficiaryAddress'))) + + return ( + Advanced (optional)}> +
+
+
+ + The onchain memo for each payment made through this address. + The project's payment feed will include the memo alongside the + payment. + + } + /> + + + + + + +
+
+ + + + +
+ {tokenMintingEnabled && + tokenAddress && + !isZeroAddress(tokenAddress) ? ( +
+ + When checked, payments made through this address will mint + the project's ERC-20 tokens. Payments will incur slightly + higher gas fees. When unchecked, the Juicebox protocol will + internally track the beneficiary's tokens, and they can + claim their ERC-20 tokens at any time. + + } + /> + + + +
+ ) : null} + + {tokenMintingEnabled ? ( +
+ + If enabled, project tokens will be minted to a custom + beneficiary address. By default, project tokens will be + minted to the wallet that pays this address. + + } + /> + { + setCustomBeneficiaryEnabled(checked) + if (!checked) { + form.setFieldsValue({ customBeneficiaryAddress: undefined }) + } + }} + checked={customBeneficiaryEnabled} + /> +
+ ) : null} + {tokenMintingEnabled && customBeneficiaryEnabled ? ( + + form.setFieldsValue({ customBeneficiaryAddress: value }) + } + /> + ) : null} + +
+
+ ) +} diff --git a/src/packages/v4/components/ProjectDashboard/components/V4ProjectToolsDrawer/PaymentAddressSection/LaunchProjectPayerModal/LaunchProjectPayerModal.tsx b/src/packages/v4/components/ProjectDashboard/components/V4ProjectToolsDrawer/PaymentAddressSection/LaunchProjectPayerModal/LaunchProjectPayerModal.tsx new file mode 100644 index 0000000000..6eca533420 --- /dev/null +++ b/src/packages/v4/components/ProjectDashboard/components/V4ProjectToolsDrawer/PaymentAddressSection/LaunchProjectPayerModal/LaunchProjectPayerModal.tsx @@ -0,0 +1,159 @@ +import { ToolOutlined } from '@ant-design/icons' +import { t, Trans } from '@lingui/macro' +import { Modal } from 'antd' +import { useForm } from 'antd/lib/form/Form' +import CopyTextButton from 'components/buttons/CopyTextButton' +import { Callout } from 'components/Callout/Callout' +import EtherscanLink from 'components/EtherscanLink' +import TransactionModal from 'components/modals/TransactionModal' +import { PROJECT_PAYER_ADDRESS_EXPLANATION } from 'components/strings' +import { providers } from 'ethers' +import { useState } from 'react' +import AdvancedOptionsCollapse from './AdvancedOptionsCollapse' + +const DEPLOY_EVENT_IDX = 0 + +/** + * Return the address of the project payer created from a `deployProjectPayer` transaction. + * @param txReceipt receipt of `deployProjectPayer` transaction + */ +const getProjectPayerAddressFromReceipt = ( + txReceipt: providers.TransactionReceipt, +): string => { + const newProjectPayerAddress = txReceipt?.logs[DEPLOY_EVENT_IDX]?.address + return newProjectPayerAddress +} + +export interface AdvancedOptionsFields { + memo: string + memoImageUrl: string | undefined + tokenMintingEnabled: boolean + customBeneficiaryAddress: string | undefined + preferClaimed: boolean +} + +export function LaunchProjectPayerModal({ + open, + onClose, + onConfirmed, +}: { + open: boolean + onClose: VoidFunction + onConfirmed?: VoidFunction +}) { + const [loadingProjectPayer, setLoadingProjectPayer] = useState() + const [transactionPending, setTransactionPending] = useState() + const [projectPayerAddress, setProjectPayerAddress] = useState() + + const [advancedOptionsForm] = useForm() + + const [confirmedModalVisible, setConfirmedModalVisible] = useState() + + // const deployProjectPayerTx = useDeployProjectPayerTx() + + // async function deployProjectPayer() { + // if (!deployProjectPayerTx) return + + // setLoadingProjectPayer(true) + + // const fields = advancedOptionsForm.getFieldsValue(true) + // const memo = [fields.memo ?? '', fields.memoImageUrl ?? ''].join(' ').trim() + + // const txSuccess = await deployProjectPayerTx( + // { + // customBeneficiaryAddress: fields.customBeneficiaryAddress, + // customMemo: memo.length > 0 ? memo : undefined, + // tokenMintingEnabled: fields.tokenMintingEnabled, + // preferClaimed: fields.preferClaimed, + // }, + // { + // onDone() { + // setTransactionPending(true) + // }, + // async onConfirmed(tx) { + // const txHash = tx?.hash + // if (!txHash) { + // return + // } + + // const txReceipt = await readProvider.getTransactionReceipt(txHash) + // const newProjectPayerAddress = + // getProjectPayerAddressFromReceipt(txReceipt) + // if (newProjectPayerAddress === undefined) { + // emitErrorNotification(t`Something went wrong.`) + // return + // } + // if (onConfirmed) onConfirmed() + // onClose() + // setProjectPayerAddress(newProjectPayerAddress) + // setLoadingProjectPayer(false) + // setTransactionPending(false) + // setConfirmedModalVisible(true) + // advancedOptionsForm.resetFields() + // }, + // }, + // ) + // if (!txSuccess) { + // setLoadingProjectPayer(false) + // setTransactionPending(false) + // } + // } + + return ( + <> + null}//deployProjectPayer} + onCancel={() => onClose()} + confirmLoading={loadingProjectPayer} + transactionPending={transactionPending} + width={600} + > +
+
{PROJECT_PAYER_ADDRESS_EXPLANATION}
+
+ + By default, the payer will receive any project tokens minted from + the payment. + +
+ + + + Contributors who pay this address from a custodial service + platform (like Coinbase){' '} + won't receive project tokens. + + + +
+
+ setConfirmedModalVisible(false)} + cancelButtonProps={{ hidden: true }} + okText={t`Done`} + centered + > +

+ Your new project payer address: +

+ {' '} + +

+ + Existing project payer addresses can be found in the Tools drawer ( + ) on the project page. + +

+
+ + ) +} diff --git a/src/packages/v4/components/ProjectDashboard/components/V4ProjectToolsDrawer/PaymentAddressSection/PaymentAddressSection.tsx b/src/packages/v4/components/ProjectDashboard/components/V4ProjectToolsDrawer/PaymentAddressSection/PaymentAddressSection.tsx new file mode 100644 index 0000000000..89e74c9358 --- /dev/null +++ b/src/packages/v4/components/ProjectDashboard/components/V4ProjectToolsDrawer/PaymentAddressSection/PaymentAddressSection.tsx @@ -0,0 +1,49 @@ +import { PROJECT_PAYER_ADDRESS_EXPLANATION } from 'components/strings' +import { LaunchProjectPayerButton } from './LaunchProjectPayerButton' + +export function PaymentAddressSection() { + // const { projectId } = useContext(ProjectMetadataContext) + + // const [projectPayersModalIsVisible, setProjectPayersModalIsVisible] = + // useState() + + // const { data, loading } = useEtherc20ProjectPayersQuery({ + // client, + // variables: { + // where: { + // projectId, + // }, + // }, + // }) + + // const projectPayers = data?.etherc20ProjectPayers + + return ( + <> +

{PROJECT_PAYER_ADDRESS_EXPLANATION}

+ +

+ {/* */} + {/* setProjectPayersModalIsVisible(false)} + projectPayers={projectPayers} + /> */} +

+ + + + ) +} diff --git a/src/packages/v4/components/ProjectDashboard/components/V4ProjectToolsDrawer/V4ProjectToolsDrawer.tsx b/src/packages/v4/components/ProjectDashboard/components/V4ProjectToolsDrawer/V4ProjectToolsDrawer.tsx new file mode 100644 index 0000000000..a6bbde9e60 --- /dev/null +++ b/src/packages/v4/components/ProjectDashboard/components/V4ProjectToolsDrawer/V4ProjectToolsDrawer.tsx @@ -0,0 +1,92 @@ +import { Trans } from '@lingui/macro' +import { Divider, Drawer } from 'antd' +import useMobile from 'hooks/useMobile' +import { AddToProjectBalanceForm } from './AddToProjectBalanceForm' + +export function V4ProjectToolsDrawer({ + open, + onClose, +}: { + open?: boolean + onClose?: VoidFunction +}) { + // const hasOFAC = projectMetadata?.projectRequiredOFACCheck + + const isMobile = useMobile() + + return ( + +

+ Tools +

+ +
+ {/* {hasOFAC ? null : ( */} + <> +
+ +
+ + + {/* @v4todo:
+

+ Project payer addresses +

+ + +
+ */} + + {/* )} */} + + {/* + groupedSplits={{ + splits: payoutSplits, + group: ETH_PAYOUT_SPLIT_GROUP, + }} + > + Export payouts CSV + + ) : undefined + } + exportReservedTokensButton={ + reservedTokensSplits ? ( + + groupedSplits={{ + splits: reservedTokensSplits, + group: RESERVED_TOKEN_SPLIT_GROUP, + }} + > + Export reserved token recipient CSV + + ) : undefined + } + /> */} + + {/* + +
+

+ Project contracts directory +

+ +

+ Browse the project's smart contract addresses.{' '} + + Go to contracts directory + + . +

+
*/} +
+
+ ) +} diff --git a/src/packages/v4/components/ProjectDashboard/components/V4ProjectToolsDrawer/index.tsx b/src/packages/v4/components/ProjectDashboard/components/V4ProjectToolsDrawer/index.tsx new file mode 100644 index 0000000000..321faa4723 --- /dev/null +++ b/src/packages/v4/components/ProjectDashboard/components/V4ProjectToolsDrawer/index.tsx @@ -0,0 +1,2 @@ +export { V4ProjectToolsDrawer } from './V4ProjectToolsDrawer'; + diff --git a/src/packages/v4/views/V4ProjectDashboard/V4ProjectHeader.tsx b/src/packages/v4/views/V4ProjectDashboard/V4ProjectHeader.tsx index db5bcef5f3..decdad49b9 100644 --- a/src/packages/v4/views/V4ProjectDashboard/V4ProjectHeader.tsx +++ b/src/packages/v4/views/V4ProjectDashboard/V4ProjectHeader.tsx @@ -11,6 +11,7 @@ import { SocialLinkButton } from 'components/Project/ProjectHeader/SocialLinkBut import { TruncatedText } from 'components/TruncatedText' import useMobile from 'hooks/useMobile' import Link from 'next/link' +import { ProjectHeaderPopupMenu } from 'packages/v4/components/ProjectDashboard/components/ProjectHeaderPopupMenu' import V4ProjectHandleLink from 'packages/v4/components/V4ProjectHandleLink' import { useV4WalletHasPermission } from 'packages/v4/hooks/useV4WalletHasPermission' import { V4OperatorPermission } from 'packages/v4/models/v4Permissions' @@ -57,8 +58,7 @@ export const V4ProjectHeader = ({ className }: { className?: string }) => {
{projectId ? ( isMobile ? ( - // - <> + ) : ( <>
@@ -73,7 +73,7 @@ export const V4ProjectHeader = ({ className }: { className?: string }) => { /> ))}
- {/* @v4todo: */} + {canQueueRuleSets && (