Skip to content

Commit ca3cb78

Browse files
author
Manuel Gellfart
authored
Release 2.2.0 (#190)
2 parents 2d8a8b7 + fbb398b commit ca3cb78

File tree

9 files changed

+334
-102
lines changed

9 files changed

+334
-102
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "safe-dao-governance-app",
33
"homepage": "https://github.com/safe-global/safe-dao-governance-app",
44
"license": "GPL-3.0",
5-
"version": "2.1.0",
5+
"version": "2.2.0",
66
"scripts": {
77
"build": "next build && next export",
88
"lint": "tsc && next lint",

src/components/Points/ActivityPointsFeed.tsx

Lines changed: 162 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -2,44 +2,83 @@ import { GLOBAL_CAMPAIGN_IDS } from '@/config/constants'
22
import { Campaign } from '@/hooks/useCampaigns'
33
import { useChainId } from '@/hooks/useChainId'
44
import { useOwnCampaignRank } from '@/hooks/useLeaderboard'
5-
import { formatDatetime } from '@/utils/date'
5+
import { formatDate } from '@/utils/date'
66
import { Divider, Skeleton, Stack, Typography } from '@mui/material'
77
import Box from '@mui/material/Box'
8-
import { useEffect, useMemo, useState } from 'react'
8+
import { ReactNode, useEffect, useMemo, useState } from 'react'
99
import PointsCounter from '../PointsCounter'
1010
import Barcode from '@/public/images/horizontal_barcode.svg'
1111
import css from './styles.module.css'
12+
import { useLatestCampaignUpdate } from '@/hooks/useLatestCampaignUpdate'
13+
14+
const BorderedBox = ({ children }: { children: ReactNode }) => {
15+
return (
16+
<Box
17+
sx={{
18+
border: ({ palette }) => `1px solid ${palette.border.light}`,
19+
borderRadius: '6px',
20+
p: '24px 32px',
21+
display: 'flex',
22+
flexDirection: 'column',
23+
gap: 2,
24+
position: 'relative',
25+
}}
26+
>
27+
{children}
28+
</Box>
29+
)
30+
}
31+
1232
const HiddenValue = () => (
1333
<Typography minWidth="80px">
1434
<Skeleton />
1535
</Typography>
1636
)
1737

38+
const DataWrapper = ({ children }: { children: ReactNode }) => {
39+
return (
40+
<Stack
41+
direction={{ xs: 'column', lg: 'row' }}
42+
justifyContent="space-between"
43+
alignItems={{ xs: 'start', lg: 'center' }}
44+
spacing={2}
45+
>
46+
{children}
47+
</Stack>
48+
)
49+
}
50+
1851
export const ActivityPointsFeed = ({ campaign }: { campaign?: Campaign }) => {
1952
const { data: ownEntry, isLoading } = useOwnCampaignRank(campaign?.resourceId)
53+
const { data: latestUpdate, isLoading: isLatestUpdateLoading } = useLatestCampaignUpdate(campaign?.resourceId)
2054

2155
const data = useMemo(() => {
2256
return {
23-
activityPoints: ownEntry?.totalPoints ?? 0,
24-
boostedPoints: ownEntry ? ownEntry.totalBoostedPoints - ownEntry.totalPoints : 0,
25-
totalPoints: ownEntry?.totalBoostedPoints ?? 0,
57+
activityPoints: latestUpdate?.totalPoints ?? 0,
58+
boostedPoints: latestUpdate ? latestUpdate.totalBoostedPoints - latestUpdate.totalPoints : 0,
59+
totalPoints: latestUpdate?.totalBoostedPoints ?? 0,
60+
overallPoints: ownEntry?.totalBoostedPoints ?? 0,
2661
}
27-
}, [ownEntry])
62+
}, [latestUpdate, ownEntry])
2863

2964
const [showBoostPoints, setShowBoostPoints] = useState(false)
3065
const [showTotalPoints, setShowTotalPoints] = useState(false)
66+
const [showOverallPoints, setShowOverallPoints] = useState(false)
3167

3268
useEffect(() => {
33-
if (ownEntry !== undefined || !isLoading) {
34-
const showBoostPointsTimeout = setTimeout(() => setShowBoostPoints(true), 1000)
35-
const showTotalPointsTimeout = setTimeout(() => setShowTotalPoints(true), 2000)
69+
if (isLoading || isLatestUpdateLoading) {
70+
return
71+
}
72+
const showBoostPointsTimeout = setTimeout(() => setShowBoostPoints(true), 1000)
73+
const showTotalPointsTimeout = setTimeout(() => setShowTotalPoints(true), 2000)
74+
const showOverallPointsTimeout = setTimeout(() => setShowOverallPoints(true), 3000)
3675

37-
return () => {
38-
clearTimeout(showBoostPointsTimeout)
39-
clearTimeout(showTotalPointsTimeout)
40-
}
76+
return () => {
77+
clearTimeout(showBoostPointsTimeout)
78+
clearTimeout(showTotalPointsTimeout)
79+
clearTimeout(showOverallPointsTimeout)
4180
}
42-
}, [ownEntry, isLoading])
81+
}, [ownEntry, isLoading, isLatestUpdateLoading])
4382

4483
const chainId = useChainId()
4584

@@ -53,81 +92,120 @@ export const ActivityPointsFeed = ({ campaign }: { campaign?: Campaign }) => {
5392

5493
if (isLoading) {
5594
return (
56-
<Box
57-
key={campaign?.resourceId}
58-
sx={{
59-
border: ({ palette }) => `1px solid ${palette.border.light}`,
60-
borderRadius: '6px',
61-
p: '24px 32px',
62-
display: 'flex',
63-
flexDirection: 'column',
64-
gap: 2,
65-
position: 'relative',
66-
}}
67-
>
68-
<Stack direction="row" justifyContent="space-between" alignItems="center" spacing={2}>
69-
<Typography color="text.secondary">Activity points</Typography>
70-
<HiddenValue />
71-
</Stack>
72-
<Stack direction="row" justifyContent="space-between" alignItems="center" spacing={2}>
73-
<Typography color="text.secondary">Boost points</Typography>
74-
<HiddenValue />
75-
</Stack>
76-
<Divider />
77-
<Stack direction="row" justifyContent="space-between" alignItems="center" spacing={2}>
78-
<Typography color="text.secondary">{!isGlobal && 'Campaign'} Total</Typography>
79-
<HiddenValue />
80-
</Stack>
81-
<HiddenValue />
82-
<Barcode className={css.barcode} />
83-
</Box>
95+
<>
96+
<BorderedBox key={campaign?.resourceId}>
97+
<DataWrapper>
98+
<Typography color="text.secondary">Last drop</Typography>
99+
<HiddenValue />
100+
</DataWrapper>
101+
<Divider
102+
sx={{
103+
marginRight: '-32px',
104+
marginLeft: '-32px',
105+
}}
106+
/>
107+
<DataWrapper>
108+
<Typography color="text.secondary">Activity points</Typography>
109+
<HiddenValue />
110+
</DataWrapper>
111+
<DataWrapper>
112+
<Typography color="text.secondary">Boost points</Typography>
113+
<HiddenValue />
114+
</DataWrapper>
115+
<Divider />
116+
<DataWrapper>
117+
<Typography color="text.secondary">{!isGlobal && 'Campaign'} Week total</Typography>
118+
<HiddenValue />
119+
</DataWrapper>
120+
<Divider />
121+
<DataWrapper>
122+
<Typography color="text.secondary">{!isGlobal && 'Campaign'} Overall</Typography>
123+
<HiddenValue />
124+
</DataWrapper>
125+
<Typography mt={6} alignSelf="center" color="text.secondary">
126+
Your points are updated weekly.
127+
</Typography>
128+
<Barcode className={css.barcode} />
129+
</BorderedBox>
130+
{!isGlobal && (
131+
<BorderedBox>
132+
<DataWrapper>
133+
<Typography color="text.secondary">Campaign total</Typography>
134+
<HiddenValue />
135+
</DataWrapper>
136+
</BorderedBox>
137+
)}
138+
</>
84139
)
85140
}
86141

87142
return (
88-
<Box
89-
key={campaign?.resourceId}
90-
sx={{
91-
border: ({ palette }) => `1px solid ${palette.border.light}`,
92-
borderRadius: '6px',
93-
p: '24px 32px',
94-
display: 'flex',
95-
flexDirection: 'column',
96-
gap: 2,
97-
position: 'relative',
98-
}}
99-
>
100-
<Stack direction="row" justifyContent="space-between" alignItems="center" spacing={2}>
101-
<Typography color="text.secondary">Activity points</Typography>
102-
<PointsCounter value={Number(data.activityPoints)} fontWeight={700}>
103-
points
104-
</PointsCounter>
105-
</Stack>
106-
<Stack direction="row" justifyContent="space-between" alignItems="center" spacing={2}>
107-
<Typography color="text.secondary">Boost points</Typography>
108-
{showBoostPoints ? (
109-
<PointsCounter value={Number(data.boostedPoints)} fontWeight={700}>
110-
points
111-
</PointsCounter>
112-
) : (
113-
<HiddenValue />
114-
)}
115-
</Stack>
116-
<Divider />
117-
<Stack direction="row" justifyContent="space-between" alignItems="center" spacing={2}>
118-
<Typography color="text.secondary">{!isGlobal && 'Campaign'} Total</Typography>
119-
{showTotalPoints ? (
120-
<PointsCounter value={Number(data.totalPoints)} fontWeight={700} fontSize="27px" color="primary">
143+
<>
144+
<BorderedBox key={campaign?.resourceId}>
145+
<DataWrapper>
146+
<Typography color="text.secondary">Your last drop</Typography>
147+
{latestUpdate && (
148+
<Typography color="text.secondary">
149+
{formatDate(new Date(latestUpdate.startDate))} - {formatDate(new Date(latestUpdate.endDate))}
150+
</Typography>
151+
)}
152+
</DataWrapper>
153+
<Divider
154+
sx={{
155+
marginRight: '-32px',
156+
marginLeft: '-32px',
157+
}}
158+
/>
159+
<DataWrapper>
160+
<Typography color="text.secondary">Activity points</Typography>
161+
<PointsCounter value={Number(data.activityPoints)} fontWeight={700}>
121162
points
122163
</PointsCounter>
123-
) : (
124-
<HiddenValue />
125-
)}
126-
</Stack>
127-
<Typography mt={6} alignSelf="center" color="text.secondary">
128-
Last updated {formatDatetime(new Date(campaign.lastUpdated))}
129-
</Typography>
130-
<Barcode className={css.barcode} />
131-
</Box>
164+
</DataWrapper>
165+
<DataWrapper>
166+
<Typography color="text.secondary">Boost points</Typography>
167+
{showBoostPoints ? (
168+
<PointsCounter value={Number(data.boostedPoints)} fontWeight={700}>
169+
points
170+
</PointsCounter>
171+
) : (
172+
<HiddenValue />
173+
)}
174+
</DataWrapper>
175+
<Divider />
176+
<DataWrapper>
177+
<Typography>{!isGlobal && 'Campaign'} Week total</Typography>
178+
{showTotalPoints ? (
179+
<PointsCounter value={Number(data.totalPoints)} fontWeight={700}>
180+
points
181+
</PointsCounter>
182+
) : (
183+
<HiddenValue />
184+
)}
185+
</DataWrapper>
186+
<Typography mt={6} alignSelf="center" color="text.secondary">
187+
Points are updated weekly.
188+
</Typography>
189+
<Typography mt={-2} alignSelf="center" color="text.secondary">
190+
Last update: {formatDate(new Date(campaign.lastUpdated))}
191+
</Typography>
192+
<Barcode className={css.barcode} />
193+
</BorderedBox>
194+
195+
{!isGlobal && (
196+
<BorderedBox>
197+
<DataWrapper>
198+
<Typography color="text.secondary">Campaign total</Typography>
199+
{showOverallPoints ? (
200+
<PointsCounter value={Number(data.overallPoints)} fontWeight={700}>
201+
points
202+
</PointsCounter>
203+
) : (
204+
<HiddenValue />
205+
)}
206+
</DataWrapper>
207+
</BorderedBox>
208+
)}
209+
</>
132210
)
133211
}

src/components/Points/CampaignTabs.tsx

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,44 @@
1-
import * as React from 'react'
21
import Tabs from '@mui/material/Tabs'
32
import Tab from '@mui/material/Tab'
4-
import { Box, Chip, Typography, useMediaQuery } from '@mui/material'
3+
import { Box, Chip, Tooltip, Typography } from '@mui/material'
54
import { useTheme } from '@mui/material/styles'
5+
import css from './styles.module.css'
66

77
const CAMPAIGN_TABS = [
88
{
9-
label: 'Global',
9+
label: (
10+
<Typography display="flex" flexDirection="row" gap={1} alignItems="center" fontWeight={700}>
11+
<Tooltip title="This campaign is active now" arrow>
12+
<Box
13+
sx={{
14+
borderRadius: '100%',
15+
backgroundColor: ({ palette }) => palette.primary.main,
16+
minWidth: '6px',
17+
minHeight: '6px',
18+
flexShrink: 0,
19+
marginRight: 1,
20+
}}
21+
/>
22+
</Tooltip>
23+
Global
24+
</Typography>
25+
),
1026
disabled: false,
1127
},
1228
{
1329
label: (
14-
<Typography display="flex" flexDirection="row" gap={1} alignItems="center">
15-
Campaigns <Chip variant="outlined" sx={{ borderRadius: '4px' }} label="soon" />
30+
<Typography display="flex" flexDirection="row" gap={1} alignItems="center" fontWeight={700}>
31+
<Box
32+
sx={{
33+
borderRadius: '100%',
34+
backgroundColor: ({ palette }) => palette.text.disabled,
35+
minWidth: '6px',
36+
minHeight: '6px',
37+
flexShrink: 0,
38+
marginRight: 1,
39+
}}
40+
/>
41+
Campaigns <Chip variant="outlined" className={css.comingSoon} label="soon" />
1642
</Typography>
1743
),
1844
disabled: true,
@@ -21,19 +47,22 @@ const CAMPAIGN_TABS = [
2147

2248
const CampaignTabs = ({ onChange, selectedTabIdx }: { onChange: (tab: number) => void; selectedTabIdx: number }) => {
2349
const theme = useTheme()
24-
const isSmallScreen = useMediaQuery(theme.breakpoints.down('lg'))
2550
return (
2651
<Box padding="8px 0px">
2752
<Tabs
28-
orientation={isSmallScreen ? 'horizontal' : 'vertical'}
53+
orientation="vertical"
2954
variant="scrollable"
3055
value={selectedTabIdx}
3156
aria-label="Vertical tabs example"
32-
sx={{ borderRight: 1, borderColor: 'divider' }}
57+
sx={{ border: 1, borderColor: 'divider', borderRadius: '6px' }}
3358
onChange={(_, value) => onChange(value)}
3459
>
3560
{CAMPAIGN_TABS.map((tab, tabIdx) => (
36-
<Tab key={tabIdx} {...tab} />
61+
<Tab
62+
sx={{ textTransform: 'none', fontWeight: 700, textAlign: 'left', alignItems: 'start' }}
63+
key={tabIdx}
64+
{...tab}
65+
/>
3766
))}
3867
</Tabs>
3968
</Box>

0 commit comments

Comments
 (0)