diff --git a/package-lock.json b/package-lock.json
index 26e08e9d26..e942c55b88 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -51,7 +51,6 @@
"react-router-dom": "^6.22.0",
"react-toastify": "^9.0.8",
"remark-gfm": "^4.0.0",
- "remove-markdown": "^0.5.0",
"viem": "^2.8.12",
"vite": "^5.1.0",
"vite-plugin-checker": "^0.6.4",
@@ -25565,11 +25564,6 @@
"url": "https://opencollective.com/unified"
}
},
- "node_modules/remove-markdown": {
- "version": "0.5.0",
- "resolved": "https://registry.npmjs.org/remove-markdown/-/remove-markdown-0.5.0.tgz",
- "integrity": "sha512-x917M80K97K5IN1L8lUvFehsfhR8cYjGQ/yAMRI9E7JIKivtl5Emo5iD13DhMr+VojzMCiYk8V2byNPwT/oapg=="
- },
"node_modules/remove-trailing-separator": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
diff --git a/package.json b/package.json
index 704e178eda..493d49132a 100644
--- a/package.json
+++ b/package.json
@@ -47,11 +47,10 @@
"react-router-dom": "^6.22.0",
"react-toastify": "^9.0.8",
"remark-gfm": "^4.0.0",
- "remove-markdown": "^0.5.0",
"viem": "^2.8.12",
"vite": "^5.1.0",
- "vite-plugin-node-polyfills": "^0.21.0",
"vite-plugin-checker": "^0.6.4",
+ "vite-plugin-node-polyfills": "^0.21.0",
"wagmi": "^2.5.11",
"yup": "^1"
},
diff --git a/src/assets/css/Markdown.css b/src/assets/css/Markdown.css
index b4ce42947b..cefcd720c5 100644
--- a/src/assets/css/Markdown.css
+++ b/src/assets/css/Markdown.css
@@ -4,6 +4,7 @@
.markdown-body {
min-width: 100%;
+ max-width: 0;
}
.markdown-body,
.markdown-body > * {
diff --git a/src/components/Activity/ActivityDescription.tsx b/src/components/Activity/ActivityDescription.tsx
index 88aeaae158..6e4703c4e0 100644
--- a/src/components/Activity/ActivityDescription.tsx
+++ b/src/components/Activity/ActivityDescription.tsx
@@ -1,4 +1,4 @@
-import { Box, Flex } from '@chakra-ui/react';
+import { Box } from '@chakra-ui/react';
import { useGetMetadata } from '../../hooks/DAO/proposal/useGetMetadata';
import { Activity, FractalProposal, SnapshotProposal } from '../../types';
import Markdown from '../ui/proposal/Markdown';
@@ -12,30 +12,35 @@ interface IActivityDescription {
export function ActivityDescription({ activity, showFullDescription }: IActivityDescription) {
const metaData = useGetMetadata(activity as FractalProposal);
+
const snapshotProposal = activity as SnapshotProposal;
+
const description = snapshotProposal.description || metaData.description;
return (
-
-
+
{description && (
)}
- {!!activity.transaction && }
-
+
+ {!!activity.transaction && }
+
+
);
}
diff --git a/src/components/Activity/ActivityDescriptionGovernance.tsx b/src/components/Activity/ActivityDescriptionGovernance.tsx
index 7ef99cd291..dcfa243ce4 100644
--- a/src/components/Activity/ActivityDescriptionGovernance.tsx
+++ b/src/components/Activity/ActivityDescriptionGovernance.tsx
@@ -1,15 +1,17 @@
-import { Text } from '@chakra-ui/react';
+import { Box, Flex, Text } from '@chakra-ui/react';
import { useTranslation } from 'react-i18next';
import { useGetMetadata } from '../../hooks/DAO/proposal/useGetMetadata';
+import useDisplayName from '../../hooks/utils/useDisplayName';
import {
Activity,
GovernanceActivity,
MultisigProposal,
ActivityEventType,
- TreasuryActivity,
SnapshotProposal,
FractalProposal,
+ AzoriusProposal,
} from '../../types';
+import Avatar from '../ui/page/Header/Avatar';
const formatId = (proposalId: string) => {
if (proposalId.startsWith('0x')) {
@@ -36,33 +38,77 @@ function OnChainRejectionMessage({ activity }: { activity: Activity }) {
);
}
-export function ProposalTitle({ activity }: { activity: Activity }) {
- const { t } = useTranslation(['common', 'dashboard']);
+function ProposalAuthor({ activity }: { activity: Activity }) {
+ const snapshotProposal = activity as SnapshotProposal;
+ const azoriusProposal = activity as AzoriusProposal;
+ const multisigProposal = activity as MultisigProposal;
+ const isSnapshotProposal = !!snapshotProposal.snapshotProposalId;
+ const isAzoriusProposal = !!azoriusProposal.proposer;
+
+ const proposer = isAzoriusProposal
+ ? azoriusProposal.proposer
+ : isSnapshotProposal
+ ? snapshotProposal.author
+ : multisigProposal.confirmations[0].owner;
+
+ const { displayName: author } = useDisplayName(proposer);
+ return (
+
+
+ {author}
+
+ );
+}
+
+export function ProposalTitle({
+ activity,
+ showAuthor = false,
+}: {
+ activity: Activity;
+ showAuthor?: boolean;
+}) {
const metaData = useGetMetadata(activity as FractalProposal);
if (activity.eventType !== ActivityEventType.Governance) {
return null;
}
- if ((activity as SnapshotProposal).snapshotProposalId) {
- return (
- <>
- {formatId((activity as SnapshotProposal).snapshotProposalId)}
- {(activity as SnapshotProposal).title}
- >
- );
- }
-
- const treasuryActivity = activity as TreasuryActivity;
- const hasTransfers =
- treasuryActivity.transferAddresses && !!treasuryActivity.transferAddresses.length;
+ // Check if it's a SnapshotProposal and set variables accordingly
+ const isSnapshotProposal = (activity as SnapshotProposal).snapshotProposalId !== undefined;
+ const proposalIdText = isSnapshotProposal
+ ? formatId((activity as SnapshotProposal).snapshotProposalId)
+ : formatId((activity as GovernanceActivity).proposalId);
+ const proposaltitleText = isSnapshotProposal
+ ? (activity as SnapshotProposal).title
+ : metaData.title || '';
+ const titleText = proposalIdText + ' ' + proposaltitleText;
return (
- <>
- {formatId((activity as GovernanceActivity).proposalId)}
- {metaData.title ? {metaData.title} : null}
- {hasTransfers && {t('proposalDescriptionCont', { ns: 'dashboard' })} }
-
- >
+
+
+ {titleText}
+ {showAuthor && }
+
+
+
+
+
);
}
diff --git a/src/components/Activity/ActivityDescriptionModule.tsx b/src/components/Activity/ActivityDescriptionModule.tsx
index dead54af51..fe1c8de28b 100644
--- a/src/components/Activity/ActivityDescriptionModule.tsx
+++ b/src/components/Activity/ActivityDescriptionModule.tsx
@@ -1,34 +1,60 @@
-import { Flex, Text } from '@chakra-ui/react';
+import { Box, Flex, Text } from '@chakra-ui/react';
+import { format } from 'date-fns';
import { useTranslation } from 'react-i18next';
-import { FractalProposal } from '../../types';
+import { useFractal } from '../../providers/App/AppProvider';
+import { ActivityEventType, FractalProposal } from '../../types';
+import { DEFAULT_DATE_FORMAT } from '../../utils';
import { ActivityAddress } from './ActivityAddress';
export function ActivityDescriptionModule({ activity }: { activity: FractalProposal }) {
- const { t } = useTranslation(['treasury', 'dashboard']);
+ const { t } = useTranslation(['treasury', 'dashboard', 'common']);
+
+ const {
+ node: { daoAddress },
+ } = useFractal();
+ const eventDateLabel = t(
+ activity.eventType === ActivityEventType.Treasury
+ ? activity.transaction?.to === daoAddress
+ ? 'received'
+ : 'sent'
+ : 'created',
+ );
return (
-
- {t('moduleDescription', { ns: 'dashboard', count: activity.targets.length })}
- {activity.targets.length > 2 ? (
-
- {t('addresses', {
- ns: 'treasury',
- numOfAddresses: activity.targets.length,
- })}
-
- ) : (
- activity.targets.map((address, i, arr) => (
-
- ))
- )}
-
+
+ {t('moduleDescription', { ns: 'dashboard', count: activity.targets.length })}
+
+ {activity.targets.length > 2
+ ? t('addresses', {
+ ns: 'treasury',
+ numOfAddresses: activity.targets.length,
+ })
+ : activity.targets.map((address, i, arr) => (
+
+ ))}
+
+
+
+ {activity.eventDate && (
+
+ {eventDateLabel} {format(activity.eventDate, DEFAULT_DATE_FORMAT)}
+
+ )}
+
+
);
}
diff --git a/src/components/Activity/ActivityDescriptionTreasury.tsx b/src/components/Activity/ActivityDescriptionTreasury.tsx
index 9866ab31d7..50a21b9185 100644
--- a/src/components/Activity/ActivityDescriptionTreasury.tsx
+++ b/src/components/Activity/ActivityDescriptionTreasury.tsx
@@ -1,4 +1,4 @@
-import { Text } from '@chakra-ui/react';
+import { Flex, Text } from '@chakra-ui/react';
import { useTranslation } from 'react-i18next';
import { Activity, TreasuryActivity, ActivityEventType } from '../../types';
import { ActivityAddress } from './ActivityAddress';
@@ -26,27 +26,29 @@ export function ActivityDescriptionTreasury({ activity }: { activity: Activity }
? t('from')
: t('to');
+ const textString = transferTypeStr
+ ? transferTypeStr +
+ ' ' +
+ treasuryActivity.transferAmountTotals.join(', ') +
+ ' ' +
+ transferDirStr
+ : undefined;
+
return (
- <>
-
- {transferTypeStr} {treasuryActivity.transferAmountTotals.join(', ')} {transferDirStr}
-
- {treasuryActivity.transferAddresses.length > 2 ? (
-
- {t('addresses', {
+
+ {textString}
+ {treasuryActivity.transferAddresses.length > 2
+ ? t('addresses', {
ns: 'treasury',
numOfAddresses: treasuryActivity.transferAddresses.length,
- })}
-
- ) : (
- treasuryActivity.transferAddresses.map((address, i, arr) => (
-
- ))
- )}
- >
+ })
+ : treasuryActivity.transferAddresses.map((address, i, arr) => (
+
+ ))}
+
);
}
diff --git a/src/components/Activity/ActivityGovernance.tsx b/src/components/Activity/ActivityGovernance.tsx
deleted file mode 100644
index 812ec81667..0000000000
--- a/src/components/Activity/ActivityGovernance.tsx
+++ /dev/null
@@ -1,64 +0,0 @@
-import { Flex } from '@chakra-ui/react';
-import { format } from 'date-fns';
-import { useTranslation } from 'react-i18next';
-import { Link } from 'react-router-dom';
-import { DAO_ROUTES } from '../../constants/routes';
-import { useFractal } from '../../providers/App/AppProvider';
-import { FractalProposal, ActivityEventType, SnapshotProposal } from '../../types';
-import { DEFAULT_DATE_FORMAT } from '../../utils/numberFormats';
-import { ProposalAction } from '../Proposals/ProposalActions/ProposalAction';
-import { VoteContextProvider } from '../Proposals/ProposalVotes/context/VoteContext';
-import { Badge } from '../ui/badges/Badge';
-import { ProposalCountdown } from '../ui/proposal/ProposalCountdown';
-
-import { ActivityCard } from './ActivityCard';
-import { ActivityDescription } from './ActivityDescription';
-
-export function ActivityGovernance({ activity }: { activity: FractalProposal }) {
- const {
- node: { safe },
- } = useFractal();
- const { t } = useTranslation();
-
- const eventDateLabel = t(
- activity.eventType === ActivityEventType.Treasury
- ? activity.transaction?.to === safe?.address
- ? 'received'
- : 'sent'
- : 'created',
- );
-
- const {
- node: { daoAddress },
- } = useFractal();
-
- return (
-
-
- )
- }
- description={}
- RightElement={
-
-
-
-
-
-
- }
- eventDate={format(activity.eventDate, DEFAULT_DATE_FORMAT)}
- eventDateLabel={eventDateLabel}
- isSnapshot={!!(activity as SnapshotProposal).snapshotProposalId}
- />
-
- );
-}
diff --git a/src/components/Activity/ActivityModule.tsx b/src/components/Activity/ActivityModule.tsx
index d1cdf1e422..c0bb90cb3e 100644
--- a/src/components/Activity/ActivityModule.tsx
+++ b/src/components/Activity/ActivityModule.tsx
@@ -1,48 +1,23 @@
-import { Button } from '@chakra-ui/react';
-import { ArrowAngleUp } from '@decent-org/fractal-ui';
-import { format } from 'date-fns';
-import { useTranslation } from 'react-i18next';
import { FractalProposal } from '../../types';
-import { DEFAULT_DATE_FORMAT } from '../../utils/numberFormats';
import { Badge } from '../ui/badges/Badge';
import EtherscanLinkTransaction from '../ui/links/EtherscanLinkTransaction';
import { ActivityCard } from './ActivityCard';
import { ActivityDescriptionModule } from './ActivityDescriptionModule';
export function ActivityModule({ activity }: { activity: FractalProposal }) {
- const { t } = useTranslation('common');
return (
-
- )
- }
- description={}
- RightElement={
- !!activity.transactionHash && (
-
-
- }
- >
- {t('labelEtherscan')}
-
-
- )
- }
- eventDate={format(activity.eventDate, DEFAULT_DATE_FORMAT)}
- />
+
+
+ )
+ }
+ description={}
+ />
+
);
}
diff --git a/src/components/Proposals/ProposalCard/ProposalCard.tsx b/src/components/Proposals/ProposalCard/ProposalCard.tsx
new file mode 100644
index 0000000000..d4796291b0
--- /dev/null
+++ b/src/components/Proposals/ProposalCard/ProposalCard.tsx
@@ -0,0 +1,76 @@
+import { Box, Flex, Image, Text } from '@chakra-ui/react';
+import { format } from 'date-fns';
+import { useTranslation } from 'react-i18next';
+import { Link } from 'react-router-dom';
+import { DAO_ROUTES } from '../../../constants/routes';
+import { useFractal } from '../../../providers/App/AppProvider';
+import { ActivityEventType, FractalProposal, SnapshotProposal } from '../../../types';
+import { DEFAULT_DATE_FORMAT } from '../../../utils';
+import { ActivityDescription } from '../../Activity/ActivityDescription';
+import { Badge } from '../../ui/badges/Badge';
+import QuorumBadge from '../../ui/badges/QuorumBadge';
+
+function ProposalCard({ proposal }: { proposal: FractalProposal }) {
+ const {
+ node: { daoAddress },
+ } = useFractal();
+ const { t } = useTranslation('common');
+
+ const eventDateLabel = t(
+ proposal.eventType === ActivityEventType.Treasury
+ ? proposal.transaction?.to === daoAddress
+ ? 'received'
+ : 'sent'
+ : 'created',
+ );
+
+ const isSnapshotProposal = !!(proposal as SnapshotProposal).snapshotProposalId;
+
+ return (
+
+
+ {/* Top Row */}
+
+
+
+ {isSnapshotProposal && (
+
+ )}
+
+
+
+
+
+ {proposal.eventDate && (
+
+ {eventDateLabel} {format(proposal.eventDate, DEFAULT_DATE_FORMAT)}
+
+ )}
+
+
+
+ );
+}
+
+export default ProposalCard;
diff --git a/src/components/Proposals/ProposalsList.tsx b/src/components/Proposals/ProposalsList.tsx
index f180efc871..3323b96814 100644
--- a/src/components/Proposals/ProposalsList.tsx
+++ b/src/components/Proposals/ProposalsList.tsx
@@ -5,9 +5,9 @@ import { DAO_ROUTES } from '../../constants/routes';
import useSubmitProposal from '../../hooks/DAO/proposal/useSubmitProposal';
import { useFractal } from '../../providers/App/AppProvider';
import { FractalProposal } from '../../types';
-import { ActivityGovernance } from '../Activity/ActivityGovernance';
import { EmptyBox } from '../ui/containers/EmptyBox';
import { InfoBoxLoader } from '../ui/loaders/InfoBoxLoader';
+import ProposalCard from './ProposalCard/ProposalCard';
export function ProposalsList({ proposals }: { proposals: FractalProposal[] }) {
const {
@@ -27,9 +27,9 @@ export function ProposalsList({ proposals }: { proposals: FractalProposal[] }) {
) : proposals.length > 0 ? (
proposals.map(proposal => (
-
))
) : (
diff --git a/src/components/pages/DaoDashboard/Activities/index.tsx b/src/components/pages/DaoDashboard/Activities/index.tsx
index f21e469066..aecd7dd7f8 100644
--- a/src/components/pages/DaoDashboard/Activities/index.tsx
+++ b/src/components/pages/DaoDashboard/Activities/index.tsx
@@ -3,9 +3,9 @@ import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useFractal } from '../../../../providers/App/AppProvider';
import { ActivityEventType, SortBy, TreasuryActivity, FractalProposal } from '../../../../types';
-import { ActivityGovernance } from '../../../Activity/ActivityGovernance';
import { ActivityModule } from '../../../Activity/ActivityModule';
import { ActivityTreasury } from '../../../Activity/ActivityTreasury';
+import ProposalCard from '../../../Proposals/ProposalCard/ProposalCard';
import { EmptyBox } from '../../../ui/containers/EmptyBox';
import { InfoBoxLoader } from '../../../ui/loaders/InfoBoxLoader';
import { Sort } from '../../../ui/utils/Sort';
@@ -48,9 +48,9 @@ export function Activities() {
{sortedActivities.map((activity, i) => {
if (activity.eventType === ActivityEventType.Governance) {
return (
-
);
}
diff --git a/src/components/ui/badges/Badge.tsx b/src/components/ui/badges/Badge.tsx
index 3c0de816ec..db5f21f4d4 100644
--- a/src/components/ui/badges/Badge.tsx
+++ b/src/components/ui/badges/Badge.tsx
@@ -1,91 +1,130 @@
-import { Flex, Text, Tooltip } from '@chakra-ui/react';
-import { ActiveTwo, Check, ClockTwo, CloseX, DoubleCheck } from '@decent-org/fractal-ui';
+import { Box, Flex, Text, Tooltip } from '@chakra-ui/react';
import { useTranslation } from 'react-i18next';
import { TOOLTIP_MAXW } from '../../../constants/common';
-import { FractalProposalState, DAOState } from '../../../types';
-import { Green, Red } from '../colors';
+import { FractalProposalState, DAOState, FractalProposal } from '../../../types';
+import { ProposalCountdown } from '../proposal/ProposalCountdown';
-type BadgeType = { [key: string]: { Icon?: any; tooltipKey?: string; bg: string; color: string } };
+type BadgeType = {
+ [key: string]: {
+ tooltipKey?: string;
+ bg: string;
+ _hover: { bg: string; textColor: string };
+ textColor: string;
+ };
+};
+
+const greenText = '#8BDA8B';
+const greenBG = '#0A320A';
+const greenHoverText = '#78D378';
+const greenHover = '#0E440E';
+
+const redText = '#FFB2B2';
+const redBG = '#640E0D';
+const redHoverText = '#FF9999';
+const redHover = '#4D0B0A';
+
+const sandBG = '#C18D5A';
+const sandHover = '#B97F46';
+const sandText = '#2C1A08';
+const sandHoverText = '#150D04 ';
+
+const grayBG = '#9A979D';
+const grayHover = '#8C8990';
+
+const freezeBG = '#A3B9EC';
+const freezeHover = '#8DA8E7';
+const freezeText = '#0D2356';
+const freezeHoverText = '#09193E';
+
+const frozenBG = '#09193E';
+const frozenText = '#D1DCF5';
+const frozenHoverText = '#BCCCF0';
+const frozenHover = '#17326E';
const BADGE_MAPPING: BadgeType = {
[FractalProposalState.ACTIVE]: {
- Icon: ActiveTwo,
tooltipKey: 'stateActiveTip',
- bg: Green._500,
- color: 'grayscale.black',
+ bg: greenBG,
+ textColor: greenText,
+ _hover: { bg: greenHover, textColor: greenHoverText },
},
[FractalProposalState.TIMELOCKED]: {
- Icon: ClockTwo,
tooltipKey: 'stateTimelockedTip',
- bg: 'sand.700',
- color: 'grayscale.black',
+ bg: greenBG,
+ textColor: greenText,
+ _hover: { bg: greenHover, textColor: greenHoverText },
},
[FractalProposalState.EXECUTED]: {
- Icon: DoubleCheck,
tooltipKey: 'stateExecutedTip',
- bg: Green._600,
- color: 'grayscale.black',
+ bg: greenBG,
+ textColor: greenText,
+ _hover: { bg: greenHover, textColor: greenHoverText },
},
[FractalProposalState.EXECUTABLE]: {
- Icon: Check,
tooltipKey: 'stateExecutableTip',
- bg: Green._500,
- color: 'grayscale.black',
+ bg: greenBG,
+ textColor: greenText,
+ _hover: { bg: greenHover, textColor: greenHoverText },
},
[FractalProposalState.FAILED]: {
- Icon: CloseX,
tooltipKey: 'stateFailedTip',
- bg: Red._600,
- color: 'grayscale.black',
+ bg: redBG,
+ textColor: redText,
+ _hover: { bg: redHover, textColor: redHoverText },
},
[FractalProposalState.TIMELOCKABLE]: {
- Icon: ClockTwo,
tooltipKey: 'stateTimelockableTip',
- bg: Green._500,
- color: 'grayscale.black',
+ bg: greenBG,
+ textColor: greenText,
+ _hover: { bg: greenHover, textColor: greenHoverText },
},
[FractalProposalState.MODULE]: {
tooltipKey: 'stateModuleTip',
- bg: 'sand.700',
- color: 'grayscale.black',
+ bg: greenBG,
+ textColor: greenText,
+ _hover: { bg: greenHover, textColor: greenHoverText },
},
[FractalProposalState.EXPIRED]: {
- Icon: ClockTwo,
tooltipKey: 'stateExpiredTip',
- bg: Red._600,
- color: 'grayscale.black',
+ bg: redBG,
+ textColor: redText,
+ _hover: { bg: redHover, textColor: redHoverText },
},
[FractalProposalState.REJECTED]: {
- Icon: CloseX,
tooltipKey: 'stateRejectedTip',
- bg: Red._600,
- color: 'grayscale.black',
+ bg: redBG,
+ textColor: redText,
+ _hover: { bg: redHover, textColor: redHoverText },
},
[FractalProposalState.PENDING]: {
- Icon: ClockTwo,
tooltipKey: 'statePendingTip',
- bg: Green._500,
- color: 'grayscale.black',
+ bg: sandBG,
+ textColor: sandText,
+ _hover: { bg: sandHover, textColor: sandHoverText },
},
[FractalProposalState.CLOSED]: {
- Icon: ClockTwo,
tooltipKey: 'stateClosedTip',
- bg: Green._600,
- color: 'grayscale.black',
+ bg: grayBG,
+ textColor: '#000',
+ _hover: { bg: grayHover, textColor: '#000' },
},
[DAOState.freezeInit]: {
- Icon: Check,
tooltipKey: 'stateFreezeInitTip',
- bg: 'blue.400',
- color: 'grayscale.black',
+ bg: freezeBG,
+ textColor: freezeText,
+ _hover: { bg: freezeHover, textColor: freezeHoverText },
},
[DAOState.frozen]: {
- Icon: DoubleCheck,
tooltipKey: 'stateFrozenTip',
- bg: 'blue.400',
- color: 'grayscale.black',
+ bg: frozenBG,
+ textColor: frozenText,
+ _hover: { bg: frozenHover, textColor: frozenHoverText },
+ },
+ ownerApproved: {
+ bg: 'sand.700',
+ textColor: sandText,
+ _hover: { bg: sandBG, textColor: sandHoverText },
},
- ownerApproved: { bg: 'sand.700', color: 'grayscale.black' },
};
type BadgeSize = { [key: string]: { minWidth: string; height: string } };
@@ -97,33 +136,53 @@ const BADGE_SIZES: BadgeSize = {
interface IBadge {
size: 'sm' | 'base';
labelKey: FractalProposalState | DAOState | string;
+ proposal?: FractalProposal;
}
-export function Badge({ labelKey, size }: IBadge) {
- const { Icon, tooltipKey, ...colors } = BADGE_MAPPING[labelKey];
+export function Badge({ labelKey, size, proposal }: IBadge) {
+ const { tooltipKey, ...colors } = BADGE_MAPPING[labelKey];
const sizes = BADGE_SIZES[size];
const { t } = useTranslation('proposal');
return (
- <>
-
+
-
+
- {!!Icon && }
- {t(labelKey)}
-
-
- >
+ {t(labelKey)}
+
+ {proposal && (
+
+ )}
+
+
);
}
diff --git a/src/components/ui/badges/QuorumBadge.tsx b/src/components/ui/badges/QuorumBadge.tsx
new file mode 100644
index 0000000000..73d6435129
--- /dev/null
+++ b/src/components/ui/badges/QuorumBadge.tsx
@@ -0,0 +1,96 @@
+import { Box, Flex } from '@chakra-ui/react';
+import { Check } from '@decent-org/fractal-ui';
+import { BigNumber } from 'ethers';
+import { useMemo } from 'react';
+import { useTranslation } from 'react-i18next';
+import { useFractal } from '../../../providers/App/AppProvider';
+import {
+ AzoriusGovernance,
+ AzoriusProposal,
+ FractalProposal,
+ SnapshotProposal,
+} from '../../../types';
+
+const quorumNotReachedColor = '#838383';
+const quorumReachedColor = '#56A355';
+export default function QuorumBadge({ proposal }: { proposal: FractalProposal }) {
+ const { governance } = useFractal();
+ const { t } = useTranslation('common');
+
+ const azoriusGovernance = governance as AzoriusGovernance;
+ const { votesToken, erc721Tokens, votingStrategy } = azoriusGovernance;
+
+ const { votesSummary } = proposal as AzoriusProposal;
+ const totalVotesCasted = useMemo(() => {
+ if (votesSummary) {
+ return votesSummary.yes.add(votesSummary.no).add(votesSummary.abstain);
+ }
+ return BigNumber.from(0);
+ }, [votesSummary]);
+
+ const votesTokenDecimalsDenominator = useMemo(
+ () => BigNumber.from(10).pow(votesToken?.decimals || 0),
+ [votesToken?.decimals],
+ );
+
+ // @dev only azorius governance has quorum
+ if ((proposal as SnapshotProposal).snapshotProposalId || !votingStrategy) {
+ return null;
+ }
+
+ if (votesToken !== undefined && erc721Tokens !== undefined) {
+ return null;
+ }
+
+ const quorumDisplay = !!votingStrategy.quorumPercentage
+ ? votingStrategy.quorumPercentage.formatted
+ : !!votingStrategy.quorumThreshold
+ ? votingStrategy.quorumThreshold.formatted
+ : null;
+
+ const strategyQuorum =
+ erc721Tokens !== undefined
+ ? votingStrategy.quorumThreshold!.value.toNumber()
+ : votesToken !== undefined
+ ? votingStrategy.quorumPercentage!.value.toNumber()
+ : 0;
+ const reachedQuorum =
+ erc721Tokens !== undefined
+ ? totalVotesCasted.sub(votesSummary.no).toString()
+ : votesToken !== undefined
+ ? totalVotesCasted.sub(votesSummary.no).div(votesTokenDecimalsDenominator).toString()
+ : '0';
+ const totalQuorum = erc721Tokens !== undefined ? strategyQuorum.toString() : 0;
+
+ const meetsQuorum = votesToken
+ ? votesToken.totalSupply
+ .div(votesTokenDecimalsDenominator)
+ .div(100)
+ .mul(strategyQuorum)
+ .toString()
+ : reachedQuorum >= totalQuorum;
+
+ const displayColor =
+ !totalVotesCasted.isZero() && meetsQuorum ? quorumReachedColor : quorumNotReachedColor;
+ return (
+
+
+
+ {t('quorum', { ns: 'common' })}
+ {quorumDisplay}
+
+
+ );
+}
diff --git a/src/components/ui/page/Header/Avatar.tsx b/src/components/ui/page/Header/Avatar.tsx
index 100a7a015f..074435bb8b 100644
--- a/src/components/ui/page/Header/Avatar.tsx
+++ b/src/components/ui/page/Header/Avatar.tsx
@@ -3,8 +3,9 @@ import { Jazzicon } from '@ukstv/jazzicon-react';
import { Suspense } from 'react';
import { useImage } from 'react-image';
-type AvatarSize = 'icon' | 'lg';
+type AvatarSize = 'icon' | 'lg' | 'sm';
const avatarSizes: { [size: string]: string } = {
+ sm: '1rem',
icon: '1.5rem',
lg: '2rem',
};
diff --git a/src/components/ui/proposal/Markdown.tsx b/src/components/ui/proposal/Markdown.tsx
index 3467492e0f..f1b63fe2fa 100644
--- a/src/components/ui/proposal/Markdown.tsx
+++ b/src/components/ui/proposal/Markdown.tsx
@@ -1,9 +1,8 @@
-import { Text, Button, Image } from '@chakra-ui/react';
+import { Button, Image, Box } from '@chakra-ui/react';
import { useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import ReactMarkdown, { Components } from 'react-markdown';
import remarkGfm from 'remark-gfm';
-import removeMd from 'remove-markdown';
import '../../../assets/css/Markdown.css';
function CustomMarkdownImage({ src, alt }: { src?: string; alt?: string }) {
@@ -45,7 +44,6 @@ export default function Markdown({ truncate, content, collapsedLines = 6 }: IMar
const [totalLines, setTotalLines] = useState(0);
const [totalLinesError, setTotalLinesError] = useState(false);
const markdownTextContainerRef = useRef(null);
- const plainText = removeMd(content);
useEffect(() => {
if (
@@ -83,22 +81,10 @@ export default function Markdown({ truncate, content, collapsedLines = 6 }: IMar
return uri;
};
- if (truncate) {
- return (
-
- {plainText}
-
- );
- }
-
return (
<>
-
@@ -110,9 +96,9 @@ export default function Markdown({ truncate, content, collapsedLines = 6 }: IMar
>
{content}
-
+
- {totalLines > collapsedLines && !totalLinesError && (
+ {totalLines > collapsedLines && !totalLinesError && !truncate && (