Skip to content

Commit

Permalink
OCT-1931: Home: Epoch results / live - connect with BE (#420)
Browse files Browse the repository at this point in the history
## Description
Check after [PR-417](#417)

## Definition of Done

1. [ ] If required, the desciption of your change is added to the [QA
changelog](https://www.notion.so/octantapp/Changelog-for-the-QA-d96fa3b411cf488bb1d8d9a598d88281)
2. [ ] Acceptance criteria are met.
3. [ ] PR is manually tested before the merge by developer(s).
    - [ ] Happy path is manually checked.
4. [ ] PR is manually tested by QA when their assistance is required
(1).
- [ ] Octant Areas & Test Cases are checked for impact and updated if
required (2).
5. [ ] Unit tests are added unless there is a reason to omit them.
6. [ ] Automated tests are added when required.
7. [ ] The code is merged.
8. [ ] Tech documentation is added / updated, reviewed and approved
(including mandatory approval by a code owner, should such exist for
changed files).
    - [ ] BE: Swagger documentation is updated.
9. [ ] When required by QA:
    - [ ] Deployed to the relevant environment.
    - [ ] Passed system tests.

---

(1) Developer(s) in coordination with QA decide whether it's required.
For small tickets introducing small changes QA assistance is most
probably not required.

(2) [Octant Areas & Test
Cases](https://docs.google.com/spreadsheets/d/1cRe6dxuKJV3a4ZskAwWEPvrFkQm6rEfyUCYwLTYw_Cc).
  • Loading branch information
jmikolajczyk authored Sep 24, 2024
2 parents c9a5b76 + 95186bf commit 05039c0
Show file tree
Hide file tree
Showing 21 changed files with 107 additions and 105 deletions.
7 changes: 6 additions & 1 deletion client/src/components/Allocation/AllocationItem/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@ export interface AllocationItemWithAllocations extends ProjectIpfsWithRewards {
export default interface AllocationItemProps
extends Omit<
AllocationItemWithAllocations,
'totalValueOfAllocations' | 'percentage' | 'numberOfDonors' | 'matchedRewards' | 'donations'
| 'totalValueOfAllocations'
| 'percentage'
| 'numberOfDonors'
| 'matchedRewards'
| 'donations'
| 'matchingFund'
> {
className?: string;
isError: boolean;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,51 +1,45 @@
import { maxBy } from 'lodash';
import React, { ReactNode, useEffect, useMemo, useState } from 'react';
import React, { FC, useEffect, useState } from 'react';

import EpochResultsBar from 'components/Home/HomeGridEpochResults/EpochResultsBar';
import EpochResultsDetails from 'components/Home/HomeGridEpochResults/EpochResultsDetails';
import { EPOCH_RESULTS_BAR_ID } from 'constants/domElementsIds';
import env from 'env';
import useProjectsIpfsWithRewards, {
ProjectIpfsWithRewards,
} from 'hooks/queries/useProjectsIpfsWithRewards';

import styles from './EpochResults.module.scss';

// TODO: replace with real data -> https://linear.app/golemfoundation/issue/OCT-1931/home-epoch-results-live-connect-with-be
const data = [
{
address: '0x576edCed7475D8F64a5e2D5227c93Ca57d7f5d20',
donations: 10n,
matching: 100n,
},
{
address: '0x53390590476dC98860316e4B46Bb9842AF55efc4',
donations: 15n,
matching: 250n,
},
{
address: '0x9531C059098e3d194fF87FebB587aB07B30B1306',
donations: 40n,
matching: 100n,
},
];

const EpochResults = (): ReactNode => {
const EpochResults: FC<{ epoch: number }> = ({ epoch }) => {
const { data: projectsIpfsWithRewards } = useProjectsIpfsWithRewards(epoch);
const [highlightedBarAddress, setHighlightedBarAddress] = useState<null | string>(null);
const { ipfsGateways } = env;

const details = useMemo(() => {
const projectData = data.find(d => d.address === highlightedBarAddress);
return projectData;
}, [highlightedBarAddress]);
const projects =
projectsIpfsWithRewards.map(props => ({
epoch,
...props,
})) || [];

const details = projects.find(d => d.address === highlightedBarAddress);

const getMaxValue = (): bigint => {
const { matching, donations } = maxBy(data, d => {
if (d.donations > d.matching) {
const { matchedRewards, donations } = maxBy(projects, d => {
if (d.donations > d.matchedRewards) {
return d.donations;
}
return d.matching;
}) as { donations: bigint; matching: bigint };
return matching > donations ? matching : donations;
return d.matchedRewards;
}) as ProjectIpfsWithRewards;
return matchedRewards > donations ? matchedRewards : donations;
};

const getBarHeightPercentage = (value: bigint) => {
return (Number(value) / Number(getMaxValue())) * 100;
const maxValue = getMaxValue();
if (!maxValue || !value) {
return 0;
}
return (Number(value) / Number(maxValue)) * 100;
};

useEffect(() => {
Expand All @@ -72,19 +66,20 @@ const EpochResults = (): ReactNode => {
return (
<div className={styles.root}>
<div className={styles.graphContainer}>
{data.map(({ address, matching, donations }) => (
{projects.map(({ address, matchedRewards, donations, profileImageSmall }) => (
<EpochResultsBar
key={address}
key={`${address}__${epoch}`}
address={address}
bottomBarHeightPercentage={getBarHeightPercentage(donations)}
imageSources={ipfsGateways.split(',').map(element => `${element}${profileImageSmall}`)}
isHighlighted={!!(highlightedBarAddress && highlightedBarAddress === address)}
isLowlighted={!!(highlightedBarAddress && highlightedBarAddress !== address)}
onClick={setHighlightedBarAddress}
topBarHeightPercentage={getBarHeightPercentage(matching)}
topBarHeightPercentage={getBarHeightPercentage(matchedRewards)}
/>
))}
</div>
<EpochResultsDetails details={highlightedBarAddress ? details : null} />
<EpochResultsDetails details={highlightedBarAddress ? details : undefined} />
</div>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
border-radius: 100%;
background-color: $color-white;
box-shadow: 0 0 2.5rem 0 $color-black-10;
z-index: $z-index-2;
z-index: $z-index-3;

.projectLogoImg {
height: 2.4rem;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import React, { FC, useEffect, useRef, useState } from 'react';

import Img from 'components/ui/Img';
import { EPOCH_RESULTS_BAR_ID } from 'constants/domElementsIds';
import env from 'env';
import useProjectsIpfs from 'hooks/queries/useProjectsIpfs';

import styles from './EpochResultsBar.module.scss';
import EpochResultsBarProps from './types';
Expand All @@ -16,8 +14,8 @@ const EpochResultsBar: FC<EpochResultsBarProps> = ({
onClick,
isHighlighted,
isLowlighted,
imageSources,
}) => {
const { ipfsGateways } = env;
const topBarRef = useRef(null);
const bottomBarRef = useRef(null);
const ref = useRef(null);
Expand All @@ -26,12 +24,6 @@ const EpochResultsBar: FC<EpochResultsBarProps> = ({

const [isProjectLogoVisible, setIsProjectLogoVisible] = useState(false);

const { data: projectIpfs } = useProjectsIpfs([address]);

const projectLogoImageSources = ipfsGateways
.split(',')
.map(element => `${element}${projectIpfs[0]?.profileImageSmall}`);

useEffect(() => {
if (!isInView) {
return;
Expand Down Expand Up @@ -83,7 +75,7 @@ const EpochResultsBar: FC<EpochResultsBarProps> = ({
x: '50%',
}}
>
<Img className={styles.projectLogoImg} sources={projectLogoImageSources} />
<Img className={styles.projectLogoImg} sources={imageSources} />
<div className={styles.triangle} />
</motion.div>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export default interface EpochResultsBarProps {
address: string;
bottomBarHeightPercentage: number;
imageSources: string[];
isHighlighted: boolean;
isLowlighted: boolean;
onClick: (address: string) => void;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import React, { FC } from 'react';
import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom';

import Button from 'components/ui/Button';
import useGetValuesToDisplay from 'hooks/helpers/useGetValuesToDisplay';
import useMediaQuery from 'hooks/helpers/useMediaQuery';
import useProjectsIpfs from 'hooks/queries/useProjectsIpfs';
import { ROOT_ROUTES } from 'routes/RootRoutes/routes';

import styles from './EpochResultsDetails.module.scss';
import EpochResultsDetailsProps from './types';
Expand All @@ -14,13 +15,16 @@ const EpochResultsDetails: FC<EpochResultsDetailsProps> = ({ details }) => {
keyPrefix: 'components.home.homeGridEpochResults',
});
const { isMobile } = useMediaQuery();
const navigate = useNavigate();
const getValuesToDisplay = useGetValuesToDisplay();

const { data: projectsIpfs } = useProjectsIpfs([details?.address], 5, details !== null);

const donationsToDisplay = details
? getValuesToDisplay({
cryptoCurrency: 'ethereum',
getFormattedEthValueProps: {
shouldIgnoreGwei: true,
shouldIgnoreWei: true,
},
showCryptoSuffix: true,
valueCrypto: details.donations,
}).primary
Expand All @@ -29,24 +33,32 @@ const EpochResultsDetails: FC<EpochResultsDetailsProps> = ({ details }) => {
const matchingToDisplay = details
? getValuesToDisplay({
cryptoCurrency: 'ethereum',
getFormattedEthValueProps: {
shouldIgnoreGwei: true,
shouldIgnoreWei: true,
},
showCryptoSuffix: true,
valueCrypto: details.donations,
valueCrypto: details.matchedRewards,
}).primary
: null;

const totalToDisplay = details
? getValuesToDisplay({
cryptoCurrency: 'ethereum',
getFormattedEthValueProps: {
shouldIgnoreGwei: true,
shouldIgnoreWei: true,
},
showCryptoSuffix: true,
valueCrypto: details.donations + details.matching,
valueCrypto: details.totalValueOfAllocations,
}).primary
: null;

return (
<div className={styles.root}>
{details ? (
<>
<div className={styles.projectName}>{projectsIpfs?.[0]?.name}</div>
<div className={styles.projectName}>{details.name}</div>
<div className={styles.donations}>
{isMobile ? t('donationsShort') : t('donations')} {donationsToDisplay}
</div>
Expand All @@ -57,7 +69,13 @@ const EpochResultsDetails: FC<EpochResultsDetailsProps> = ({ details }) => {
{isMobile ? t('totalShort') : t('total')} {totalToDisplay}
</div>
{!isMobile && (
<Button className={styles.link} variant="link">
<Button
className={styles.link}
onClick={() =>
navigate(`${ROOT_ROUTES.project.absolute}/${details.epoch}/${details.address}`)
}
variant="link"
>
{t('clickToVisitProject')}
</Button>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { ProjectIpfsWithRewards } from 'hooks/queries/useProjectsIpfsWithRewards';

export default interface EpochResultsDetailsProps {
details?: any;
details?: ProjectIpfsWithRewards & { epoch: number };
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ const HomeGridEpochResults: FC<HomeGridEpochResultsProps> = ({ className }) => {
}
>
<div className={styles.root}>
<EpochResults />
<EpochResults epoch={epoch} />
</div>
</GridTile>
);
Expand Down
11 changes: 5 additions & 6 deletions client/src/components/Metrics/MetricsEpoch/MetricsEpoch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ const MetricsEpoch = (): ReactElement => {
(acc, curr) => acc + curr.matchedRewards,
0n,
);
const matchingFund = matchedRewards > 0n ? matchedRewards - patronsRewards : 0n;

// All metrics should be visible in the same moment (design). Skeletons are visible to the end of fetching all needed data.
const isLoading =
Expand Down Expand Up @@ -136,26 +135,26 @@ const MetricsEpoch = (): ReactElement => {
<MetricsEpochGridTotalDonations
className={styles.totalDonations}
isLoading={isLoading}
totalUserDonationsWithPatronRewards={totalUserDonationsWithPatronRewards}
totalUserDonations={sumOfDonations}
/>
<MetricsEpochGridTotalMatchingFund
className={styles.totalMatching}
isLoading={isLoading}
matchingFund={matchingFund}
matchingFund={matchedRewards}
/>
<MetricsEpochGridCurrentDonors className={styles.currentDonors} isLoading={isLoading} />
<MetricsEpochGridAverageLeverage className={styles.averageLeverage} isLoading={isLoading} />
<MetricsEpochGridDonationsVsMatching
className={styles.donationsVsMatching}
isLoading={isLoading}
matchingFund={matchingFund}
totalUserDonationsWithPatronRewards={totalUserDonationsWithPatronRewards}
matchingFund={matchedRewards}
totalUserDonations={sumOfDonations}
/>
<MetricsEpochGridDonationsVsPersonalAllocations
className={styles.donationsVsPersonal}
isLoading={isLoading}
totalPersonal={totalPersonal}
totalUserDonationsWithPatronRewards={totalUserDonationsWithPatronRewards}
totalUserDonations={sumOfDonations}
/>
{epoch < 4 && (
<MetricsEpochGridBelowThreshold
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,19 @@ import { formatUnitsBigInt } from 'utils/formatUnitsBigInt';
import MetricsEpochGridDonationsVsMatchingProps from './types';

const MetricsEpochGridDonationsVsMatching: FC<MetricsEpochGridDonationsVsMatchingProps> = ({
totalUserDonationsWithPatronRewards,
totalUserDonations,
isLoading,
matchingFund,
className,
}) => {
const { t } = useTranslation('translation', { keyPrefix: 'views.metrics' });

const totalUserDonationWithPatronRewardsNumber = parseFloat(
formatUnitsBigInt(totalUserDonationsWithPatronRewards),
);
const totalUserDonationsNumber = parseFloat(formatUnitsBigInt(totalUserDonations));
const matchingFundNumber = parseFloat(formatUnitsBigInt(matchingFund));

const donationsValue =
totalUserDonationWithPatronRewardsNumber > 0
? (totalUserDonationWithPatronRewardsNumber /
(matchingFundNumber + totalUserDonationWithPatronRewardsNumber)) *
100
totalUserDonationsNumber > 0
? (totalUserDonationsNumber / (matchingFundNumber + totalUserDonationsNumber)) * 100
: 0;

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ export default interface MetricsEpochGridDonationsVsMatchingProps {
className?: string;
isLoading: boolean;
matchingFund: bigint;
totalUserDonationsWithPatronRewards: bigint;
totalUserDonations: bigint;
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,15 @@ import MetricsEpochGridDonationsVsPersonalAllocationsProps from './types';

const MetricsEpochGridDonationsVsPersonalAllocations: FC<
MetricsEpochGridDonationsVsPersonalAllocationsProps
> = ({ totalUserDonationsWithPatronRewards, isLoading, totalPersonal, className }) => {
> = ({ totalUserDonations, isLoading, totalPersonal, className }) => {
const { t } = useTranslation('translation', { keyPrefix: 'views.metrics' });

const totalUserDonationWithPatronRewardsNumber = parseFloat(
formatUnitsBigInt(totalUserDonationsWithPatronRewards),
);
const totalUserDonationsNumber = parseFloat(formatUnitsBigInt(totalUserDonations));
const totalPersonalNumber = parseFloat(formatUnitsBigInt(totalPersonal));

const donationsValue =
totalUserDonationWithPatronRewardsNumber > 0
? (totalUserDonationWithPatronRewardsNumber /
(totalPersonalNumber + totalUserDonationWithPatronRewardsNumber)) *
100
totalUserDonationsNumber > 0
? (totalUserDonationsNumber / (totalPersonalNumber + totalUserDonationsNumber)) * 100
: 0;

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ export default interface MetricsEpochGridDonationsVsPersonalAllocationsProps {
className?: string;
isLoading: boolean;
totalPersonal: bigint;
totalUserDonationsWithPatronRewards: bigint;
totalUserDonations: bigint;
}
Loading

0 comments on commit 05039c0

Please sign in to comment.