From 6bb2c332a1cd6cc824210051cb9d17f7395ba250 Mon Sep 17 00:00:00 2001 From: aeddaqqa Date: Tue, 17 Sep 2024 01:02:06 +0100 Subject: [PATCH] fix(partnerConfiguration): fix creating CMAccount with withdraw role granted --- src/helpers/partnerConfigurationContext.tsx | 2 - src/helpers/useSmartContract.tsx | 61 +++- src/layout/RoutesSuite.tsx | 2 +- src/utils/display-utils.ts | 16 + src/views/partners/MyMessenger.tsx | 13 +- src/views/partners/Partner.tsx | 341 ++++++++++++++------ src/views/settings/Links.tsx | 15 +- 7 files changed, 331 insertions(+), 119 deletions(-) diff --git a/src/helpers/partnerConfigurationContext.tsx b/src/helpers/partnerConfigurationContext.tsx index 92d2ca6a..8a1bfbd2 100644 --- a/src/helpers/partnerConfigurationContext.tsx +++ b/src/helpers/partnerConfigurationContext.tsx @@ -90,8 +90,6 @@ export function reducer(state = initialState, action) { try { const { services } = action.payload if (!services || services.length < 1) return state - console.log({ services1: services[1][0] }) - console.log({ services2: services[1][1][2] }) let parsedServices = services[0].map((service, index) => { let capabilities = services[1][index][2].map(elem => elem) return { diff --git a/src/helpers/useSmartContract.tsx b/src/helpers/useSmartContract.tsx index 2473d95d..305d61ec 100644 --- a/src/helpers/useSmartContract.tsx +++ b/src/helpers/useSmartContract.tsx @@ -44,7 +44,6 @@ export const SmartContractProvider: React.FC = ({ ch return { success: false, error: 'Account is not initialized' } } try { - console.log({ state, cmAccountAddress }) for (const service of state.services) { const tx = await accountWritableContract.addService( service.name, @@ -68,11 +67,11 @@ export const SmartContractProvider: React.FC = ({ ch await offChainPaymentTx.wait() } if (state.isCam) { - const tx = await accountWriteContract.addSupportedToken(ethers.ZeroAddress) + const tx = await accountWritableContract.addSupportedToken(ethers.ZeroAddress) await tx.wait() } - const WITHDRAWER_ROLE = await readFromContract('account', 'WITHDRAWER_ROLE') - const tx = await accountWriteContract.grantRole(WITHDRAWER_ROLE, wallet.address) + const WITHDRAWER_ROLE = await accountReadOnlyContract.WITHDRAWER_ROLE() + const tx = await accountWritableContract.grantRole(WITHDRAWER_ROLE, wallet.address) await tx.wait() setContractCMAccountAddress(cmAccountAddress) setAccountReadContract(accountReadOnlyContract) @@ -128,6 +127,7 @@ export const SmartContractProvider: React.FC = ({ ch console.error(`Reason: ${decodedError?.name} (${decodedError?.args})`) } }, [accountWriteContract]) + const initializeEthers = async () => { const selectedNetwork = store.getters['Network/selectedNetwork'] const ethersProvider = new ethers.JsonRpcProvider( @@ -164,6 +164,58 @@ export const SmartContractProvider: React.FC = ({ ch if (contractCMAccountAddress) initializeCMAccountContract() }, [provider, contractCMAccountAddress]) + const getCMAccountMappings = useCallback(async () => { + try { + const mappings = new Map() + const CMACCOUNT_ROLE = await readFromContract('manager', 'CMACCOUNT_ROLE') + const roleMemberCount = await readFromContract( + 'manager', + 'getRoleMemberCount', + CMACCOUNT_ROLE, + ) + + const promises = [] + for (let i = 0; i < roleMemberCount; i++) { + promises.push( + managerReadContract.getRoleMember(CMACCOUNT_ROLE, i).then(async role => { + const creator = await readFromContract( + 'manager', + 'getCMAccountCreator', + role, + ) + return { role, creator } + }), + ) + } + + const results = await Promise.all(promises) + results.forEach(({ role, creator }) => { + mappings.set(role.toLowerCase(), creator.toLowerCase()) + }) + + const findAddress = query => { + query = query.toLowerCase() + if (mappings.has(query)) { + return { role: query, creator: mappings.get(query) } + } + for (const [role, creator] of mappings) { + if (creator === query) { + return { role, creator: query } + } + } + return null + } + + return { + findAddress, + getAllMappings: () => Object.fromEntries(mappings), + } + } catch (error) { + console.error('Error in getCMAccountMappings:', error) + throw error + } + }, [managerReadContract]) + const readFromContract = async ( contractType: 'manager' | 'account', method: string, @@ -204,6 +256,7 @@ export const SmartContractProvider: React.FC = ({ ch } const value = { + getCMAccountMappings, upgradeCMAccount, contractCMAccountAddress, setContractCMAccountAddress, diff --git a/src/layout/RoutesSuite.tsx b/src/layout/RoutesSuite.tsx index 3edee1b1..079baba7 100644 --- a/src/layout/RoutesSuite.tsx +++ b/src/layout/RoutesSuite.tsx @@ -116,7 +116,7 @@ export default function RoutesSuite() { } /> } /> - } /> + } /> } /> } /> } /> diff --git a/src/utils/display-utils.ts b/src/utils/display-utils.ts index 4369cfc2..829d5d3b 100644 --- a/src/utils/display-utils.ts +++ b/src/utils/display-utils.ts @@ -6,6 +6,22 @@ export interface Fund { value?: number } +export const transformServiceNames = services => { + return services + .map(service => { + const parts = service.name.split('.') + const versionIndex = parts.findIndex(part => part.startsWith('v')) + if (versionIndex !== -1) { + const relevantParts = parts.slice(versionIndex + 1) + if (relevantParts[relevantParts.length - 1] === 'Service') { + relevantParts.pop() + } + return relevantParts.join('') + } + return service + }) + .join(', ') +} // export function getStartDate(endDate: DateTime, timeframe: string): DateTime { // switch (timeframe) { // case Timeframe.DAYS_7: diff --git a/src/views/partners/MyMessenger.tsx b/src/views/partners/MyMessenger.tsx index bd1cd23e..f1b89aef 100644 --- a/src/views/partners/MyMessenger.tsx +++ b/src/views/partners/MyMessenger.tsx @@ -26,6 +26,7 @@ import { usePartnerConfig } from '../../helpers/usePartnerConfig' import { useSmartContract } from '../../helpers/useSmartContract' import useWalletBalance from '../../helpers/useWalletBalance' import { useFetchPartnerDataQuery } from '../../redux/services/partners' +import { transformServiceNames } from '../../utils/display-utils' import { Configuration } from './Configuration' const AmountInput = ({ amount, onAmountChange, onMaxAmountClick, maxAmount }) => { @@ -518,10 +519,8 @@ const MyMessenger = () => { Wanted Services - - {state.stepsConfig[2].services - .map(service => service.name) - .join(', ')} + + {transformServiceNames(state.stepsConfig[2].services)} )} @@ -530,10 +529,8 @@ const MyMessenger = () => { Offered Services - - {state.stepsConfig[1].services - .map(service => service.name) - .join(', ')} + + {transformServiceNames(state.stepsConfig[1].services)} )} diff --git a/src/views/partners/Partner.tsx b/src/views/partners/Partner.tsx index 50ecd626..2569c624 100644 --- a/src/views/partners/Partner.tsx +++ b/src/views/partners/Partner.tsx @@ -1,14 +1,22 @@ -import { Box, Button, Divider, Typography, useTheme } from '@mui/material' +import { Box, Button, Divider, IconButton, Typography, useTheme } from '@mui/material' import { mdiArrowLeft } from '@mdi/js' import Icon from '@mdi/react' +import { ContentCopy } from '@mui/icons-material' import React from 'react' import { Link, useNavigate, useParams } from 'react-router-dom' import PartnerBusinessFields from '../../components/Partners/PartnerBusinessFields' import PartnerFlag from '../../components/Partners/PartnerFlag' import PartnerLogo from '../../components/Partners/PartnerLogo' +import { usePartnerConfigurationContext } from '../../helpers/partnerConfigurationContext' import { useSmartContract } from '../../helpers/useSmartContract' +import { useAppSelector } from '../../hooks/reduxHooks' import { useFetchPartnerDataQuery } from '../../redux/services/partners' +import { + displayFirstPartLongString, + displaySecondPartLongString, + transformServiceNames, +} from '../../utils/display-utils' const ContentField = ({ label, children }) => { return ( @@ -41,6 +49,7 @@ const ContentField = ({ label, children }) => { } const Partner = () => { + const auth = useAppSelector(state => state.appConfig.isAuth) let { partnerID } = useParams() const theme = useTheme() const isDark = theme.palette.mode === 'dark' @@ -58,10 +67,103 @@ const Partner = () => { : value?.wallet?.address, }) const navigate = useNavigate() + const { state, dispatch } = usePartnerConfigurationContext() if (error || (!partner && !isFetching && !isLoading)) { navigate('/partners') return null } + + const OwnBusinsessNotOnMessenger = ( + <> + + Connect now and interact with other partners + + + For the MVP, the C-Chain address of the person which will be operating need to be + stored in Strapi prior, then this button appears. + + + + ) + + const OwnBusinessOnMessensger = ( + <> + + Camino Messenger + + + + Wanted Services + + + {transformServiceNames(state.stepsConfig[2].services)} + + + + + Offered Services + + + {transformServiceNames(state.stepsConfig[1].services)} + + + {value?.wallet?.address && ( + + + Partner address + + + + {displayFirstPartLongString(value?.wallet?.address, 28)}… + {displaySecondPartLongString(value?.wallet?.address, 28)} + + navigator.clipboard.writeText(value.wallet.address)} + size="small" + sx={{ + color: theme => `${theme.palette.text.primary} !important`, + }} + > + + + + + )} + + + ) if (isLoading || isFetching) return <> return ( @@ -136,67 +238,88 @@ const Partner = () => { theme.palette.background.paper }, - ]} + sx={{ + display: 'flex', + flexDirection: 'column', + gap: '16px', + flex: '0 1 30%', + maxWidth: { xs: '100%', md: '350px' }, + minWidth: { xs: '100%', md: '300px' }, + }} > + {auth && path.includes('partners/messenger-configuration') && ( + + theme.palette.mode === 'dark' ? '#0F182A' : '#F1F5F9', + '&::before': { + content: '""', + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + borderRadius: '12px', + padding: '2px', + background: 'linear-gradient(135deg, #B440FC 0%, #35E9AD 100%)', + WebkitMask: + 'linear-gradient(#fff 0 0) content-box, ' + + 'linear-gradient(#fff 0 0)', + WebkitMaskComposite: 'xor', + maskComposite: 'exclude', + }, + }} + > + {value?.contractCMAccountAddress + ? OwnBusinessOnMessensger + : OwnBusinsessNotOnMessenger} + + )} theme.palette.background.paper }, + ]} > - - - - - {partner.attributes.country_flag && - partner.attributes.country_flag.data?.attributes && ( - - )} - - - - theme.palette.card.text, - fontSize: '16px', - fontStyle: 'normal', - fontWeight: 400, - lineHeight: '150%', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + p: '1rem 1.5rem', }} > - {partner.attributes.contactFirstname + - ' ' + - partner.attributes.contactLastname} - - - - - + + + + + {partner.attributes.country_flag && + partner.attributes.country_flag.data?.attributes && ( + + )} + + + theme.palette.card.text, @@ -206,51 +329,73 @@ const Partner = () => { lineHeight: '150%', }} > - {partner.attributes.contactEmail} + {partner.attributes.contactFirstname + + ' ' + + partner.attributes.contactLastname} - - - - - - theme.palette.card.text, - fontSize: '16px', - fontStyle: 'normal', - fontWeight: 400, - lineHeight: '150%', - }} + + + + - {partner.attributes.contactPhone} - - - - - - - theme.palette.card.text, - fontSize: '16px', - fontStyle: 'normal', - fontWeight: 400, - lineHeight: '150%', - }} + theme.palette.card.text, + fontSize: '16px', + fontStyle: 'normal', + fontWeight: 400, + lineHeight: '150%', + }} + > + {partner.attributes.contactEmail} + + + + + + - {partner.attributes.companyWebsite} - - - + theme.palette.card.text, + fontSize: '16px', + fontStyle: 'normal', + fontWeight: 400, + lineHeight: '150%', + }} + > + {partner.attributes.contactPhone} + + + + + + + theme.palette.card.text, + fontSize: '16px', + fontStyle: 'normal', + fontWeight: 400, + lineHeight: '150%', + }} + > + {partner.attributes.companyWebsite} + + + + diff --git a/src/views/settings/Links.tsx b/src/views/settings/Links.tsx index 8693125a..08acc925 100644 --- a/src/views/settings/Links.tsx +++ b/src/views/settings/Links.tsx @@ -33,11 +33,14 @@ export default function Links({ type = 'else' }: { type?: string }) { else if (path === '/settings/manage-multisig') setValue(1) else if (path === '/settings/verify-wallet') setValue(2) else if (path === '/partners') setValue(0) - else if (path.includes('partners/messenger-configuration')) setValue(1) - else if (path.includes('overview')) setSecondValue(0) - else if (path.includes('distribution')) setSecondValue(1) - else if (path.includes('supply')) setSecondValue(2) - else setValue(0) + else if (path.includes('partners/messenger-configuration')) { + setValue(1) + if (path.includes('mymessenger')) setSecondValue(1) + else if (path.includes('mydetails')) setSecondValue(0) + else if (path.includes('distribution')) setSecondValue(3) + else if (path.includes('supply')) setSecondValue(2) + else if (path.includes('bots')) setSecondValue(4) + } else setValue(0) dispatch(changeActiveApp('Network')) }, [path]) // eslint-disable-line react-hooks/exhaustive-deps const auth = useAppSelector(state => state.appConfig.isAuth) @@ -115,7 +118,7 @@ export default function Links({ type = 'else' }: { type?: string }) { sx={tabStyle(0, secondValue)} />, navigate('/partners/messenger-configuration/messenger')} + onClick={() => navigate('/partners/messenger-configuration/mymessenger')} className="tab" disableRipple label="My Messenger Account"