Skip to content

Commit

Permalink
OCT-1972: Add load state to new Home epoch stats chart (#426)
Browse files Browse the repository at this point in the history
## Description

## 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 26, 2024
2 parents fbd3719 + 27e5cde commit c4af949
Show file tree
Hide file tree
Showing 8 changed files with 121 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
flex-direction: column;
flex: 1;

.image {
height: 11.1rem;
margin: 0 auto 0;
opacity: 0.5;
}

.graphContainer {
margin-bottom: 0.8rem;
flex: 1;
Expand All @@ -11,6 +17,11 @@
justify-content: space-between;
overflow: auto;
margin-top: -3.2rem;

&.isLoading {
margin: 0;
padding-top: 2.4rem;
}
}

.details {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,21 @@
import cx from 'classnames';
import { maxBy } from 'lodash';
import React, { FC, useEffect, useState } from 'react';

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

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

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

const projects =
projectsIpfsWithRewards.map(props => ({
epoch,
...props,
})) || [];

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

const getMaxValue = (): bigint => {
Expand Down Expand Up @@ -65,21 +59,30 @@ const EpochResults: FC<{ epoch: number }> = ({ epoch }) => {

return (
<div className={styles.root}>
<div className={styles.graphContainer}>
{projects.map(({ address, matchedRewards, donations, profileImageSmall }) => (
<EpochResultsBar
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(matchedRewards)}
/>
))}
<div className={cx(styles.graphContainer, isLoading && styles.isLoading)}>
{isLoading ? (
<Img className={styles.image} src="/images/headphones_girl.webp" />
) : (
projects.map(({ address, matchedRewards, donations, profileImageSmall, epoch }) => (
<EpochResultsBar
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(matchedRewards)}
/>
))
)}
</div>
<EpochResultsDetails details={highlightedBarAddress ? details : undefined} />
<EpochResultsDetails
details={highlightedBarAddress ? details : undefined}
isLoading={isLoading}
/>
</div>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import { ProjectIpfsWithRewards } from 'hooks/queries/useProjectsIpfsWithRewards';

export default interface EpochResultsProps {
className?: string;
isLoading?: boolean;
projects: (ProjectIpfsWithRewards & { epoch: number })[];
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
background-color: $color-octant-grey6;
border-radius: $border-radius-08;

.loading {
color: $color-octant-grey5;
font-weight: $font-weight-bold;
}

.projectName {
color: $color-octant-dark;
font-weight: $font-weight-bold;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { FC } from 'react';
import React, { FC, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom';

Expand All @@ -10,13 +10,14 @@ import { ROOT_ROUTES } from 'routes/RootRoutes/routes';
import styles from './EpochResultsDetails.module.scss';
import EpochResultsDetailsProps from './types';

const EpochResultsDetails: FC<EpochResultsDetailsProps> = ({ details }) => {
const EpochResultsDetails: FC<EpochResultsDetailsProps> = ({ details, isLoading }) => {
const { i18n, t } = useTranslation('translation', {
keyPrefix: 'components.home.homeGridEpochResults',
});
const { isMobile } = useMediaQuery();
const navigate = useNavigate();
const getValuesToDisplay = useGetValuesToDisplay();
const [dots, setDots] = useState(0);

const donationsToDisplay = details
? getValuesToDisplay({
Expand Down Expand Up @@ -54,9 +55,37 @@ const EpochResultsDetails: FC<EpochResultsDetailsProps> = ({ details }) => {
}).primary
: null;

useEffect(() => {
if (!isLoading) {
return;
}
const id = setInterval(
() =>
setDots(prev => {
if (prev === 3) {
return 0;
}
return prev + 1;
}),
300,
);
return () => {
clearInterval(id);
setDots(0);
};
}, [isLoading]);

return (
<div className={styles.root}>
{details ? (
{isLoading && (
<div className={styles.loading}>
{t('loadingChartData')}
{[...Array(dots).keys()].map(key => (
<span key={`dot__${key}`}>.</span>
))}
</div>
)}
{details && (
<>
<div className={styles.projectName}>{details.name}</div>
<div className={styles.donations}>
Expand All @@ -80,7 +109,7 @@ const EpochResultsDetails: FC<EpochResultsDetailsProps> = ({ details }) => {
</Button>
)}
</>
) : null}
)}
</div>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ import { ProjectIpfsWithRewards } from 'hooks/queries/useProjectsIpfsWithRewards

export default interface EpochResultsDetailsProps {
details?: ProjectIpfsWithRewards & { epoch: number };
isLoading?: boolean;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import cx from 'classnames';
import _first from 'lodash/first';
import React, { FC, useState } from 'react';
import React, { FC, useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';

import GridTile from 'components/shared/Grid/GridTile';
Expand All @@ -9,24 +9,56 @@ import useIsProjectAdminMode from 'hooks/helpers/useIsProjectAdminMode';
import useCurrentEpoch from 'hooks/queries/useCurrentEpoch';
import useIsDecisionWindowOpen from 'hooks/queries/useIsDecisionWindowOpen';
import useIsPatronMode from 'hooks/queries/useIsPatronMode';
import useProjectsIpfsWithRewards from 'hooks/queries/useProjectsIpfsWithRewards';
import { arrowRight } from 'svg/misc';

import EpochResults from './EpochResults';
import styles from './HomeGridEpochResults.module.scss';
import HomeGridEpochResultsProps from './types';

const HomeGridEpochResults: FC<HomeGridEpochResultsProps> = ({ className }) => {
const { data: currentEpoch } = useCurrentEpoch();
const initalLoadingRef = useRef(true);
const { data: isDecisionWindowOpen } = useIsDecisionWindowOpen();
const { data: currentEpoch } = useCurrentEpoch();
const [epoch, setEpoch] = useState<number>(currentEpoch! - 1);
const { t } = useTranslation('translation', {
keyPrefix: 'components.home.homeGridEpochResults',
});
const { data: projectsIpfsWithRewards, isFetching: isFetchingProjectsIpfsWithRewards } =
useProjectsIpfsWithRewards(epoch);
const isProjectAdminMode = useIsProjectAdminMode();
const { data: isPatronMode } = useIsPatronMode();

const isRightArrowDisabled = epoch === currentEpoch! - 1;
const isLeftArrowDisabled = epoch < 2;
const projects =
projectsIpfsWithRewards.map(props => ({
epoch,
...props,
})) || [];

const isAnyProjectDonated = projects.some(({ donations }) => donations > 0n);

const isLoading = isFetchingProjectsIpfsWithRewards && !isAnyProjectDonated;

const isRightArrowDisabled =
(isLoading && initalLoadingRef.current) || epoch === currentEpoch! - 1;
const isLeftArrowDisabled = (isLoading && initalLoadingRef.current) || epoch < 2;

useEffect(() => {
if (!isDecisionWindowOpen || isLoading || epoch !== currentEpoch! - 1 || isAnyProjectDonated) {
return;
}

setEpoch(prev => prev - 1);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isLoading]);

useEffect(() => {
if ((initalLoadingRef.current && isLoading) || !initalLoadingRef.current) {
return;
}

initalLoadingRef.current = false;
}, [isLoading]);

return (
<GridTile
Expand Down Expand Up @@ -67,7 +99,7 @@ const HomeGridEpochResults: FC<HomeGridEpochResultsProps> = ({ className }) => {
}
>
<div className={styles.root}>
<EpochResults epoch={epoch} />
<EpochResults isLoading={isLoading} projects={projects} />
</div>
</GridTile>
);
Expand Down
3 changes: 2 additions & 1 deletion client/src/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,8 @@
"donationsShort": "D",
"matchingShort": "M",
"totalShort": "T",
"clickToVisitProject": "Click to visit project"
"clickToVisitProject": "Click to visit project",
"loadingChartData": "Loading chart data "
}
},
"settings": {
Expand Down

0 comments on commit c4af949

Please sign in to comment.