Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Type Update] DAO Info Typing #2574

Merged
merged 31 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
5707413
add new DAOInfo typing
Da-Colon Nov 26, 2024
638640e
refactor: update DAOInfo structure and improve type handling in SafeC…
Da-Colon Nov 26, 2024
7fa5c29
refactor: replace nodeHierarchy with subgraphInfo in DAO components a…
Da-Colon Nov 26, 2024
ebe87c1
refactor: remove unused hooks related to Fractal modules and DAO loading
Da-Colon Nov 26, 2024
3582df2
refactor: update useFractalNode to use subgraph queries and improve D…
Da-Colon Nov 26, 2024
0ca063c
refactor: rename fractalModules to daoModules and update related refe…
Da-Colon Nov 26, 2024
1f540b6
refactor: update useDaoInfoStore to utilize subgraphInfo and adjust r…
Da-Colon Nov 27, 2024
bd13066
refactor: remove unused setDecentModules call in useUpdateSafeData hook
Da-Colon Nov 27, 2024
b0ec5db
refactor: enhance useFractalNode to integrate safeAPI and decentModul…
Da-Colon Nov 27, 2024
24fc95a
refactor: update DaoHierarchyNode to improve data loading and error h…
Da-Colon Nov 27, 2024
779f296
remove comment, let it ride for now
Da-Colon Nov 27, 2024
2c0fdd7
Merge branch 'issue/2529-cache-it' into breakout-issue/typing-dao-info
Da-Colon Nov 27, 2024
20eee2d
clean up error UI
Da-Colon Nov 27, 2024
db056a2
refactor: replace Flex with Box in DAONodeInfoCard and improve layout…
Da-Colon Nov 27, 2024
51a2c84
refactor: inline props structure in DAONodeInfoCard component
Da-Colon Nov 27, 2024
aa11d66
feat: add voting strategies to DAO hierarchy and update DAONodeInfoCard
Da-Colon Nov 28, 2024
62fb574
fix: handle null node response in DaoHierarchyNode component
Da-Colon Nov 28, 2024
dd821a1
refactor: update layout of voting strategies in DAONodeInfoCard compo…
Da-Colon Nov 28, 2024
680e8dd
refactor: rename modules to modulesAddresses for clarity in DaoInfoSt…
Da-Colon Nov 28, 2024
588301d
refactor: rename daoModules to modules for consistency across compone…
Da-Colon Nov 28, 2024
4fe6a61
refactor: replace FractalModuleData with DecentModule for improved cl…
Da-Colon Nov 28, 2024
41bfc90
refactor: remove unused lookupModules import from useUpdateSafeData hook
Da-Colon Nov 28, 2024
a5fa7f2
Merge branch 'breakout-issue/typing-dao-info' of github.com:decentdao…
Da-Colon Nov 28, 2024
21f4de6
refactor: replace FractalModuleData with DecentModule in DaoHierarchy…
Da-Colon Nov 28, 2024
77881bf
refactor: rename modules variable to decentModules for improved clari…
Da-Colon Nov 28, 2024
782c53b
Merge branch 'breakout-issue/typing-dao-info' of github.com:decentdao…
Da-Colon Nov 28, 2024
730c57c
refactor: update DAO-related types to handle null values for improved…
Da-Colon Nov 29, 2024
003be47
refactor: update safeAddress handling to ensure null safety across ho…
Da-Colon Nov 29, 2024
ed0f426
refactor: remove null type from parentAddress for improved type safet…
Da-Colon Nov 29, 2024
5a9faf6
Merge branch 'breakout-issue/typing-dao-info' of github.com:decentdao…
Da-Colon Nov 30, 2024
f698f95
Merge pull request #2579 from decentdao/issue/2530-hierarchy-voting-s…
Da-Colon Dec 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 6 additions & 10 deletions src/components/DaoCreator/formComponents/AzoriusGovernance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,12 @@ import { DAOCreateMode } from './EstablishEssentials';
export function AzoriusGovernance(props: ICreationStepProps) {
const { values, setFieldValue, isSubmitting, transactionPending, isSubDAO, mode } = props;

const {
safe,
nodeHierarchy: { parentAddress },
fractalModules,
} = useDaoInfoStore();
const { safe, subgraphInfo, modules } = useDaoInfoStore();

const fractalModule = useMemo(
() => fractalModules.find(_module => _module.moduleType === FractalModuleType.FRACTAL),
[fractalModules],
);
const fractalModule = useMemo(() => {
if (!modules) return null;
return modules.find(_module => _module.moduleType === FractalModuleType.FRACTAL);
}, [modules]);

const [showCustomNonce, setShowCustomNonce] = useState<boolean>();
const { t } = useTranslation(['daoCreate', 'common']);
Expand Down Expand Up @@ -189,7 +185,7 @@ export function AzoriusGovernance(props: ICreationStepProps) {
</Alert>
</Flex>
</StepWrapper>
{!!parentAddress && (
{!!subgraphInfo?.parentAddress && (
<Box
padding="1.5rem"
bg="neutral-2"
Expand Down
27 changes: 19 additions & 8 deletions src/components/DaoCreator/formComponents/EstablishEssentials.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,27 +24,38 @@ export function EstablishEssentials(props: ICreationStepProps) {
const { t } = useTranslation(['daoCreate', 'common']);
const { values, setFieldValue, isSubmitting, transactionPending, isSubDAO, errors, mode } = props;

const { daoName, daoSnapshotENS, safe } = useDaoInfoStore();
const { subgraphInfo, safe } = useDaoInfoStore();

const isEdit = mode === DAOCreateMode.EDIT;

const safeAddress = safe?.address;

useEffect(() => {
if (isEdit) {
setFieldValue('essentials.daoName', daoName, false);
if (safeAddress && createAccountSubstring(safeAddress) !== daoName) {
if (isEdit && subgraphInfo) {
setFieldValue('essentials.daoName', subgraphInfo.daoName, false);
if (safeAddress && createAccountSubstring(safeAddress) !== subgraphInfo.daoName) {
// Pre-fill the snapshot URL form field when editing
setFieldValue('essentials.snapshotENS', daoSnapshotENS || '', false);
setFieldValue('essentials.snapshotENS', subgraphInfo.daoSnapshotENS || '', false);
}
}
}, [setFieldValue, mode, daoName, daoSnapshotENS, isEdit, safeAddress]);
}, [
setFieldValue,
mode,
subgraphInfo?.daoName,
subgraphInfo?.daoSnapshotENS,
isEdit,
safeAddress,
subgraphInfo,
]);

const daoNameDisabled =
isEdit && !!daoName && !!safeAddress && createAccountSubstring(safeAddress) !== daoName;
isEdit &&
!!subgraphInfo?.daoName &&
!!safeAddress &&
createAccountSubstring(safeAddress) !== subgraphInfo?.daoName;

// If in governance edit mode and snapshot URL is already set, disable the field
const snapshotENSDisabled = isEdit && !!daoSnapshotENS;
const snapshotENSDisabled = isEdit && !!subgraphInfo?.daoSnapshotENS;

const handleGovernanceChange = (value: string) => {
if (value === GovernanceType.AZORIUS_ERC20) {
Expand Down
6 changes: 3 additions & 3 deletions src/components/DaoDashboard/Info/ParentLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,19 @@ import { useDaoInfoStore } from '../../../store/daoInfo/useDaoInfoStore';
* Displays a link to the current DAO's parent, if it has one.
*/
export function ParentLink() {
const { nodeHierarchy } = useDaoInfoStore();
const { subgraphInfo } = useDaoInfoStore();
const { addressPrefix } = useNetworkConfig();
const { t } = useTranslation('breadcrumbs');

if (!nodeHierarchy.parentAddress) {
if (!subgraphInfo?.parentAddress) {
return null;
}

return (
<Link
color="celery-0"
_hover={{ textDecoration: 'none', color: 'celery--6' }}
to={DAO_ROUTES.dao.relative(addressPrefix, nodeHierarchy.parentAddress)}
to={DAO_ROUTES.dao.relative(addressPrefix, subgraphInfo.parentAddress)}
marginBottom="1rem"
as={RouterLink}
width="fit-content"
Expand Down
132 changes: 88 additions & 44 deletions src/components/DaoHierarchy/DaoHierarchyNode.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,23 @@
import { Center, Flex, Icon, Link } from '@chakra-ui/react';
import { useLazyQuery } from '@apollo/client';
import { Center, Flex, Icon, Link, Text } from '@chakra-ui/react';
import { ArrowElbowDownRight } from '@phosphor-icons/react';
import { useEffect, useState } from 'react';
import { useCallback, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Link as RouterLink } from 'react-router-dom';
import { Address } from 'viem';
import { useChainId } from 'wagmi';
import { DAOQueryDocument } from '../../../.graphclient';
import { DAO_ROUTES } from '../../constants/routes';
import { useLoadDAONode } from '../../hooks/DAO/loaders/useLoadDAONode';
import { useDecentModules } from '../../hooks/DAO/loaders/useDecentModules';
import { CacheKeys } from '../../hooks/utils/cache/cacheDefaults';
import { setValue, getValue } from '../../hooks/utils/cache/useLocalStorage';
import { useSafeAPI } from '../../providers/App/hooks/useSafeAPI';
import { useNetworkConfig } from '../../providers/NetworkConfig/NetworkConfigProvider';
import { useDaoInfoStore } from '../../store/daoInfo/useDaoInfoStore';
import { DaoHierarchyInfo, DaoInfo, WithError } from '../../types';
import { DaoHierarchyInfo } from '../../types';
import { DAONodeInfoCard, NODE_HEIGHT_REM } from '../ui/cards/DAONodeInfoCard';
import { BarLoader } from '../ui/loaders/BarLoader';

function transformNode(node: DaoInfo, safeAddress: Address): DaoHierarchyInfo {
return {
daoName: node.daoName,
safeAddress: safeAddress,
nodeHierarchy: {
parentAddress: node.nodeHierarchy.parentAddress,
childNodes: node.nodeHierarchy.childNodes.map(child => {
if (!child.safe) {
throw new Error('Child node does not have a safe');
}
return {
safeAddress: child.safe.address,
};
}),
},
};
}

/**
* A recursive component that displays a "hierarchy" of DAOInfoCards.
*
Expand All @@ -49,10 +35,51 @@ export function DaoHierarchyNode({
depth: number;
}) {
const { safe: currentSafe } = useDaoInfoStore();
const { t } = useTranslation('common');
const safeApi = useSafeAPI();
const [hierarchyNode, setHierarchyNode] = useState<DaoHierarchyInfo>();
const { addressPrefix } = useNetworkConfig();
const [hasErrorLoading, setErrorLoading] = useState<boolean>(false);
const { addressPrefix, subgraph } = useNetworkConfig();
const chainId = useChainId();
const { loadDao } = useLoadDAONode();
const lookupModules = useDecentModules();

const [getDAOInfo] = useLazyQuery(DAOQueryDocument, {
context: {
subgraphSpace: subgraph.space,
subgraphSlug: subgraph.slug,
subgraphVersion: subgraph.version,
},
});

const loadDao = useCallback(
async (_safeAddress: Address): Promise<DaoHierarchyInfo | undefined> => {
if (!safeApi) {
throw new Error('Safe API not ready');
}
try {
const safe = await safeApi.getSafeInfo(_safeAddress);
const graphRawNodeData = await getDAOInfo({ variables: { safeAddress: _safeAddress } });
const modules = await lookupModules(safe.modules);
const graphDAOData = graphRawNodeData.data?.daos[0];
if (!graphRawNodeData || !graphDAOData) {
throw new Error('No data found');
}
return {
daoName: graphDAOData.name ?? null,
safeAddress: _safeAddress,
parentAddress: graphDAOData.parentAddress ?? null,
childAddresses: graphDAOData.hierarchy.map(child => child.address),
daoSnapshotENS: graphDAOData.snapshotENS ?? null,
proposalTemplatesHash: graphDAOData.proposalTemplatesHash ?? null,
modules,
};
} catch (e) {
setErrorLoading(true);
return;
}
},
[getDAOInfo, lookupModules, safeApi],
);

useEffect(() => {
if (safeAddress) {
Expand All @@ -66,22 +93,16 @@ export function DaoHierarchyNode({
return;
}
loadDao(safeAddress).then(_node => {
const errorNode = _node as WithError;
if (!errorNode.error) {
const fnode = _node as DaoInfo;
const theNode = transformNode(fnode, safeAddress);
setValue(
{
cacheName: CacheKeys.HIERARCHY_DAO_INFO,
chainId,
daoAddress: safeAddress,
},
theNode,
);
setHierarchyNode(theNode);
} else if (errorNode.error === 'errorFailedSearch') {
setHierarchyNode(undefined);
}
if (!_node) return;
setValue(
{
cacheName: CacheKeys.HIERARCHY_DAO_INFO,
chainId,
daoAddress: safeAddress,
},
_node,
);
setHierarchyNode(_node);
});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
Expand All @@ -100,6 +121,29 @@ export function DaoHierarchyNode({
);
}

if (hasErrorLoading) {
return (
<Flex
w="full"
bg="neutral-2"
p="1.5rem"
width="100%"
borderRadius="0.75rem"
border="1px"
borderColor="transparent"
>
<Center w="100%">
<Text
textStyle="label-base"
color="red-0"
>
{t('errorMySafesNotLoaded')}
</Text>
</Center>
</Flex>
);
}

const isCurrentViewingDAO =
currentSafe?.address.toLowerCase() === hierarchyNode.safeAddress.toLocaleLowerCase();
return (
Expand Down Expand Up @@ -128,23 +172,23 @@ export function DaoHierarchyNode({
</Link>

{/* CHILD NODES */}
{hierarchyNode?.nodeHierarchy.childNodes.map(childNode => {
{hierarchyNode?.childAddresses.map(childAddress => {
return (
<Flex
minH={`${NODE_HEIGHT_REM}rem`}
key={childNode.safeAddress}
key={childAddress}
gap="1.25rem"
>
<Icon
as={ArrowElbowDownRight}
my={`${NODE_HEIGHT_REM / 2.5}rem`}
ml="0.5rem"
boxSize="32px"
color={currentSafe?.address === childNode.safeAddress ? 'celery-0' : 'neutral-6'}
color={currentSafe?.address === childAddress ? 'celery-0' : 'neutral-6'}
/>

<DaoHierarchyNode
safeAddress={childNode.safeAddress}
safeAddress={childAddress}
depth={depth + 1}
/>
</Flex>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Box, Button, Text, Flex } from '@chakra-ui/react';
import { abis } from '@fractal-framework/fractal-contracts';
import { useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { getAddress, getContract, isHex } from 'viem';
import { getAddress, getContract, isHex, zeroAddress } from 'viem';
import { useAccount, useWalletClient } from 'wagmi';
import GnosisSafeL2Abi from '../../../assets/abi/GnosisSafeL2';
import { Check } from '../../../assets/theme/custom/icons/Check';
Expand Down Expand Up @@ -50,7 +50,7 @@ export function TxActions({ proposal }: { proposal: MultisigProposal }) {
const { loadSafeMultisigProposals } = useSafeMultisigProposals();
const { data: walletClient } = useWalletClient();

const isOwner = safe?.owners?.includes(userAccount.address || '');
const isOwner = safe?.owners?.includes(userAccount.address ?? zeroAddress);

if (!isOwner) return null;

Expand Down
6 changes: 3 additions & 3 deletions src/components/Proposals/ProposalInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export function ProposalInfo({
}) {
const metaData = useGetMetadata(proposal);
const { t } = useTranslation('proposal');
const { daoSnapshotENS } = useDaoInfoStore();
const { subgraphInfo } = useDaoInfoStore();
const { snapshotProposal } = useSnapshotProposal(proposal);

const [modalType, props] = useMemo(() => {
Expand Down Expand Up @@ -62,10 +62,10 @@ export function ProposalInfo({
showIcon={false}
textColor="neutral-7"
/>
{snapshotProposal && (
{snapshotProposal && subgraphInfo && (
<>
<SnapshotButton
snapshotENS={`${daoSnapshotENS}/proposal/${snapshotProposal.proposalId}`}
snapshotENS={`${subgraphInfo.daoSnapshotENS}/proposal/${snapshotProposal.proposalId}`}
/>
{(proposal as ExtendedSnapshotProposal).privacy === 'shutter' && (
<Button
Expand Down
6 changes: 3 additions & 3 deletions src/components/Proposals/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export default function Proposals() {
governance: { type },
guardContracts,
} = useFractal();
const { daoSnapshotENS } = useDaoInfoStore();
const { subgraphInfo } = useDaoInfoStore();

const [sortBy, setSortBy] = useState<SortBy>(SortBy.Newest);
const [filters, setFilters] = useState<FractalProposalState[]>([]);
Expand All @@ -73,12 +73,12 @@ export default function Proposals() {
}
break;
}
if (daoSnapshotENS) {
if (subgraphInfo?.daoSnapshotENS) {
options = [...options, ...FILTERS_SNAPSHOT];
}
setAllOptions(options);
setFilters(options);
}, [daoSnapshotENS, guardContracts.freezeGuardContractAddress, type]);
}, [subgraphInfo?.daoSnapshotENS, guardContracts.freezeGuardContractAddress, type]);

const toggleFilter = (filter: FractalProposalState) => {
setFilters(prevState => {
Expand Down
4 changes: 2 additions & 2 deletions src/components/SafeSettings/SettingsNavigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export default function SettingsNavigation() {
const { t } = useTranslation('settings');
const { addressPrefix } = useNetworkConfig();
const { governance } = useFractal();
const { safe, fractalModules } = useDaoInfoStore();
const { safe, modules } = useDaoInfoStore();
const azoriusGovernance = governance as AzoriusGovernance;

return (
Expand Down Expand Up @@ -143,7 +143,7 @@ export default function SettingsNavigation() {
textStyle="body-base"
color="neutral-7"
>
{fractalModules.length + safe.guard ? 1 : 0}
{(modules ?? []).length + (safe?.guard ? 1 : 0)}
</Text>
</SettingsLink>
<SettingsLink
Expand Down
Loading