Skip to content

Commit 5972950

Browse files
committed
add validator thumbnail pictures
1 parent 2d7d9b4 commit 5972950

File tree

5 files changed

+154
-27
lines changed

5 files changed

+154
-27
lines changed

examples/stake-tokens/components/react/all-validators.tsx

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
Text,
2525
Image,
2626
useColorMode,
27+
Center,
2728
} from '@chakra-ui/react';
2829
import {
2930
DelegateWarning,
@@ -46,20 +47,52 @@ import type {
4647
import { TransactionResult } from '../types';
4748
import { ChainName } from '@cosmos-kit/core';
4849

50+
export const Thumbnail = ({
51+
identity,
52+
name,
53+
thumbnailUrl,
54+
}: {
55+
identity: string | undefined;
56+
name: string | undefined;
57+
thumbnailUrl: string;
58+
}) => {
59+
return (
60+
<>
61+
{identity && thumbnailUrl ? (
62+
<Image
63+
borderRadius="full"
64+
boxSize="30px"
65+
src={thumbnailUrl}
66+
alt={name}
67+
mr={2}
68+
/>
69+
) : (
70+
<Center boxSize="30px" bgColor="gray.400" borderRadius="full" mr={2}>
71+
{name && name.slice(0, 1).toUpperCase()}
72+
</Center>
73+
)}
74+
</>
75+
);
76+
};
77+
4978
const AllValidators = ({
5079
validators,
5180
balance,
5281
delegations,
5382
updateData,
5483
unbondingDays,
5584
chainName,
85+
thumbnails,
5686
}: {
5787
validators: Validator[];
5888
balance: number;
5989
delegations: Delegation[];
6090
updateData: () => void;
6191
unbondingDays: number;
6292
chainName: ChainName;
93+
thumbnails: {
94+
[key: string]: string;
95+
};
6396
}) => {
6497
const { isOpen, onOpen, onClose } = useDisclosure();
6598
const { getSigningStargateClient, address } = useChain(chainName);
@@ -161,7 +194,11 @@ const AllValidators = ({
161194

162195
<ModalBody>
163196
<ValidatorInfo
164-
imgUrl="https://wallet.keplr.app/_next/image?url=https%3A%2F%2Fs3.amazonaws.com%2Fkeybase_processed_uploads%2F909034c1d36c1d1f3e9191f668007805_360_360.jpeg&w=64&q=75"
197+
imgUrl={
198+
currentValidator?.description?.identity
199+
? thumbnails[currentValidator.description.identity]
200+
: ''
201+
}
165202
name={currentValidator?.description?.moniker || ''}
166203
commission={5}
167204
apr={22.08}
@@ -223,13 +260,15 @@ const AllValidators = ({
223260
overflowX="hidden"
224261
>
225262
<Text mr={4}>{index + 1}</Text>
226-
{/* <Image
227-
borderRadius="full"
228-
boxSize="30px"
229-
src={validator.imgUrl}
230-
alt={validator.description.moniker}
231-
mr={2}
232-
/> */}
263+
<Thumbnail
264+
identity={validator.description?.identity}
265+
name={validator.description?.moniker}
266+
thumbnailUrl={
267+
validator.description?.identity
268+
? thumbnails[validator.description.identity]
269+
: ''
270+
}
271+
/>
233272
<Text>{validator?.description?.moniker}</Text>
234273
</Box>
235274
</Td>

examples/stake-tokens/components/react/delegate-modal.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
UnorderedList,
2323
useColorModeValue,
2424
useToast,
25+
Center,
2526
} from '@chakra-ui/react';
2627
import { TransactionResult } from '../types';
2728

@@ -37,7 +38,13 @@ export const ValidatorInfo = ({
3738
apr: number;
3839
}) => (
3940
<Flex alignItems="center" gap={4} mb={4}>
40-
<Image borderRadius="full" boxSize="60px" src={imgUrl} alt={name} />
41+
{imgUrl ? (
42+
<Image borderRadius="full" boxSize="60px" src={imgUrl} alt={name} />
43+
) : (
44+
<Center boxSize="60px" borderRadius="full" bgColor="gray.400">
45+
{name.slice(0, 1).toUpperCase()}
46+
</Center>
47+
)}
4148
<Stack>
4249
<Heading as="h4" size="md">
4350
{name}

examples/stake-tokens/components/react/my-validators.tsx

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import type {
4747
} from 'interchain/types/codegen/cosmos/staking/v1beta1/staking';
4848
import type { DelegationDelegatorReward as Reward } from 'interchain/types/codegen/cosmos/distribution/v1beta1/distribution';
4949
import { ChainName } from '@cosmos-kit/core';
50+
import { Thumbnail } from './all-validators';
5051

5152
const MyValidators = ({
5253
validators,
@@ -57,6 +58,7 @@ const MyValidators = ({
5758
updateData,
5859
unbondingDays,
5960
chainName,
61+
thumbnails,
6062
}: {
6163
validators: Validator[];
6264
allValidator: Validator[];
@@ -66,6 +68,9 @@ const MyValidators = ({
6668
updateData: () => void;
6769
unbondingDays: number;
6870
chainName: ChainName;
71+
thumbnails: {
72+
[key: string]: string;
73+
};
6974
}) => {
7075
const { getSigningStargateClient, address } = useChain(chainName);
7176

@@ -152,6 +157,7 @@ const MyValidators = ({
152157
return {
153158
details: validator?.description?.details,
154159
name: validator?.description?.moniker,
160+
identity: validator?.description?.identity,
155161
address: validator.operatorAddress,
156162
staked: exponentiate(delegation.balance!.amount, -exp),
157163
reward: Number(exponentiate(rewardAmount, -exp).toFixed(6)),
@@ -379,7 +385,11 @@ const MyValidators = ({
379385

380386
<ModalBody>
381387
<ValidatorInfo
382-
imgUrl="https://wallet.keplr.app/_next/image?url=https%3A%2F%2Fs3.amazonaws.com%2Fkeybase_processed_uploads%2F909034c1d36c1d1f3e9191f668007805_360_360.jpeg&w=64&q=75"
388+
imgUrl={
389+
currentValidator?.identity
390+
? thumbnails[currentValidator.identity]
391+
: ''
392+
}
383393
name={currentValidator?.name || ''}
384394
commission={5}
385395
apr={22.08}
@@ -427,7 +437,11 @@ const MyValidators = ({
427437

428438
<ModalBody>
429439
<ValidatorInfo
430-
imgUrl="https://wallet.keplr.app/_next/image?url=https%3A%2F%2Fs3.amazonaws.com%2Fkeybase_processed_uploads%2F909034c1d36c1d1f3e9191f668007805_360_360.jpeg&w=64&q=75"
440+
imgUrl={
441+
currentValidator?.identity
442+
? thumbnails[currentValidator.identity]
443+
: ''
444+
}
431445
name={currentValidator?.name || ''}
432446
commission={5}
433447
apr={22.08}
@@ -474,7 +488,11 @@ const MyValidators = ({
474488

475489
<ModalBody>
476490
<ValidatorInfo
477-
imgUrl="https://wallet.keplr.app/_next/image?url=https%3A%2F%2Fs3.amazonaws.com%2Fkeybase_processed_uploads%2F909034c1d36c1d1f3e9191f668007805_360_360.jpeg&w=64&q=75"
491+
imgUrl={
492+
currentValidator?.identity
493+
? thumbnails[currentValidator.identity]
494+
: ''
495+
}
478496
name={currentValidator?.name || ''}
479497
commission={5}
480498
apr={22.08}
@@ -548,13 +566,15 @@ const MyValidators = ({
548566
overflowX="hidden"
549567
>
550568
<Text mr={4}>{index + 1}</Text>
551-
{/* <Image
552-
borderRadius="full"
553-
boxSize="30px"
554-
src={validator.imgUrl}
555-
alt={validator.description.moniker}
556-
mr={2}
557-
/> */}
569+
<Thumbnail
570+
identity={validator.description?.identity}
571+
name={validator.description?.moniker}
572+
thumbnailUrl={
573+
validator.description?.identity
574+
? thumbnails[validator.description.identity]
575+
: ''
576+
}
577+
/>
558578
<Text>{validator?.description?.moniker}</Text>
559579
</Box>
560580
</Td>
@@ -645,13 +665,13 @@ const MyValidators = ({
645665
overflowX="hidden"
646666
>
647667
<Text mr={4}>{index + 1}</Text>
648-
{/* <Image
649-
borderRadius="full"
650-
boxSize="30px"
651-
src={validator.imgUrl}
652-
alt={validator.name}
653-
mr={2}
654-
/> */}
668+
<Thumbnail
669+
identity={validator.identity}
670+
name={validator.name}
671+
thumbnailUrl={
672+
validator.identity ? thumbnails[validator.identity] : ''
673+
}
674+
/>
655675
<Text>{validator.name}</Text>
656676
</Box>
657677
</Td>

examples/stake-tokens/components/react/staking.tsx

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,15 @@ export const getExponent = (chainName: string) => {
2929
)?.exponent as number;
3030
};
3131

32+
const splitIntoChunks = (arr: any[], chunkSize: number) => {
33+
const res = [];
34+
for (let i = 0; i < arr.length; i += chunkSize) {
35+
const chunk = arr.slice(i, i + chunkSize);
36+
res.push(chunk);
37+
}
38+
return res;
39+
};
40+
3241
interface StakingTokens {
3342
balance: number;
3443
rewards: Reward[];
@@ -38,6 +47,9 @@ interface StakingTokens {
3847
myValidators: Validator[];
3948
allValidators: Validator[];
4049
unbondingDays: number;
50+
thumbnails: {
51+
[key: string]: string;
52+
};
4153
}
4254

4355
export const StakingSection = ({ chainName }: { chainName: ChainName }) => {
@@ -52,6 +64,7 @@ export const StakingSection = ({ chainName }: { chainName: ChainName }) => {
5264
myValidators: [],
5365
allValidators: [],
5466
unbondingDays: 0,
67+
thumbnails: {},
5568
});
5669

5770
const coin = getCoin(chainName);
@@ -68,6 +81,7 @@ export const StakingSection = ({ chainName }: { chainName: ChainName }) => {
6881
myValidators: [],
6982
allValidators: [],
7083
unbondingDays: 0,
84+
thumbnails: {},
7185
});
7286
return;
7387
}
@@ -145,6 +159,49 @@ export const StakingSection = ({ chainName }: { chainName: ChainName }) => {
145159
? Number((params?.unbondingTime?.seconds.low / 86400).toFixed(0))
146160
: 0;
147161

162+
// THUMBNAILS
163+
const validatorThumbnails = localStorage.getItem(
164+
`${chainName}-validator-thumbnails`
165+
);
166+
167+
let thumbnails = {};
168+
169+
if (validatorThumbnails) {
170+
thumbnails = JSON.parse(validatorThumbnails);
171+
} else {
172+
const identities = allValidators.map(
173+
(validator) => validator.description!.identity
174+
);
175+
176+
const chunkedIdentities = splitIntoChunks(identities, 30);
177+
178+
let responses: any[] = [];
179+
180+
for (const chunk of chunkedIdentities) {
181+
const thumbnailRequests = chunk.map((identity) => {
182+
const url = `https://keybase.io/_/api/1.0/user/lookup.json?key_suffix=${identity}&fields=pictures`;
183+
return fetch(url).then((response) => response.json());
184+
});
185+
responses = [...responses, await Promise.all(thumbnailRequests)];
186+
await new Promise((resolve) => setTimeout(resolve, 1000));
187+
}
188+
189+
const thumbnailUrls = responses
190+
.flat()
191+
.map((value) => value.them?.[0]?.pictures?.primary.url);
192+
193+
thumbnails = thumbnailUrls.reduce(
194+
(prev, cur, idx) =>
195+
identities[idx] && cur ? { ...prev, [identities[idx]]: cur } : prev,
196+
{}
197+
);
198+
199+
localStorage.setItem(
200+
`${chainName}-validator-thumbnails`,
201+
JSON.stringify(thumbnails)
202+
);
203+
}
204+
148205
setData({
149206
rewards,
150207
totalReward,
@@ -154,9 +211,10 @@ export const StakingSection = ({ chainName }: { chainName: ChainName }) => {
154211
myValidators,
155212
allValidators,
156213
unbondingDays,
214+
thumbnails,
157215
});
158216
setIsLoading(false);
159-
}, [address, coin, exp, getRpcEndpoint]);
217+
}, [address, chainName, coin.base, exp, getRpcEndpoint]);
160218

161219
useEffect(() => {
162220
getData();
@@ -193,6 +251,7 @@ export const StakingSection = ({ chainName }: { chainName: ChainName }) => {
193251
updateData={getData}
194252
unbondingDays={data.unbondingDays}
195253
chainName={chainName}
254+
thumbnails={data.thumbnails}
196255
/>
197256
</Skeleton>
198257
)}
@@ -205,6 +264,7 @@ export const StakingSection = ({ chainName }: { chainName: ChainName }) => {
205264
updateData={getData}
206265
unbondingDays={data.unbondingDays}
207266
chainName={chainName}
267+
thumbnails={data.thumbnails}
208268
/>
209269
</Skeleton>
210270
)}

examples/stake-tokens/components/types.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,4 +96,5 @@ export interface MyValidator {
9696
address: string;
9797
staked: number;
9898
reward: number;
99+
identity: string | undefined;
99100
}

0 commit comments

Comments
 (0)