Merge pull request #517 from zenodeapp/master

Optimized avatars and added avatar auto-fixer when cache is old.
This commit is contained in:
ping 2023-12-02 08:19:05 +08:00 committed by GitHub
commit 1aee5ac969
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 94 additions and 50 deletions

View File

@ -79,6 +79,7 @@ const selfRate = computed(() => {
} }
return '-'; return '-';
}); });
const logo = (identity?: string) => { const logo = (identity?: string) => {
if (!identity) return ''; if (!identity) return '';
const url = avatars.value[identity] || ''; const url = avatars.value[identity] || '';
@ -86,25 +87,44 @@ const logo = (identity?: string) => {
? url ? url
: `https://s3.amazonaws.com/keybase_processed_uploads/${url}`; : `https://s3.amazonaws.com/keybase_processed_uploads/${url}`;
}; };
const fetchAvatar = (identity: string) => {
// fetch avatar from keybase
return new Promise<void>((resolve) => {
staking
.keybase(identity)
.then((d) => {
if (Array.isArray(d.them) && d.them.length > 0) {
const uri = String(d.them[0]?.pictures?.primary?.url).replace(
'https://s3.amazonaws.com/keybase_processed_uploads/',
''
);
avatars.value[identity] = uri;
resolve();
} else throw new Error(`failed to fetch avatar for ${identity}.`);
})
.catch((error) => {
// console.error(error); // uncomment this if you want the user to see if the avatar failed to load.
resolve();
});
});
};
const loadAvatar = (identity: string) => {
// fetches avatar from keybase and stores it in localStorage
fetchAvatar(identity).then(() => {
localStorage.setItem('avatars', JSON.stringify(avatars.value));
});
};
onMounted(() => { onMounted(() => {
if (validator) { if (validator) {
staking.fetchValidator(validator).then((res) => { staking.fetchValidator(validator).then((res) => {
v.value = res.validator; v.value = res.validator;
identity.value = res.validator?.description?.identity || ''; identity.value = res.validator?.description?.identity || '';
if (identity.value && !avatars.value[identity.value]) { if (identity.value && !avatars.value[identity.value]) loadAvatar(identity.value);
staking.keybase(identity.value).then((d) => {
if (Array.isArray(d.them) && d.them.length > 0) {
const uri = String(d.them[0]?.pictures?.primary?.url).replace(
'https://s3.amazonaws.com/keybase_processed_uploads/',
''
);
if (uri) {
avatars.value[identity.value] = uri;
localStorage.setItem('avatars', JSON.stringify(avatars.value));
}
}
});
}
const prefix = valoperToPrefix(v.value.operator_address) || '<Invalid>'; const prefix = valoperToPrefix(v.value.operator_address) || '<Invalid>';
addresses.value.hex = consensusPubkeyToHexAddress( addresses.value.hex = consensusPubkeyToHexAddress(
v.value.consensus_pubkey v.value.consensus_pubkey
@ -184,13 +204,18 @@ function pageload(p: number) {
<div class="w-24 rounded-lg absolute opacity-10"></div> <div class="w-24 rounded-lg absolute opacity-10"></div>
<div class="w-24 rounded-lg"> <div class="w-24 rounded-lg">
<img <img
v-if="avatars[identity] !== 'undefined'" v-if="identity && avatars[identity] !== 'undefined'"
v-lazy="logo(identity)" v-lazy="logo(identity)"
class="object-contain" class="object-contain"
@error="
(e) => {
loadAvatar(identity);
}
"
/> />
<Icon <Icon
v-else v-else
class="text-4xl" class="text-8xl"
:icon="`mdi-help-circle-outline`" :icon="`mdi-help-circle-outline`"
/> />
</div> </div>

View File

@ -141,39 +141,52 @@ const list = computed(() => {
return unbondList.value.map((x, i) => ({v: x, rank: 'primary', logo: logo(x.description.identity)})); return unbondList.value.map((x, i) => ({v: x, rank: 'primary', logo: logo(x.description.identity)}));
}); });
const fetchAvatar = (identity: string) => {
// fetch avatar from keybase
return new Promise<void>((resolve) => {
staking
.keybase(identity)
.then((d) => {
if (Array.isArray(d.them) && d.them.length > 0) {
const uri = String(d.them[0]?.pictures?.primary?.url).replace(
'https://s3.amazonaws.com/keybase_processed_uploads/',
''
);
avatars.value[identity] = uri;
resolve();
} else throw new Error(`failed to fetch avatar for ${identity}`);
})
.catch((error) => {
// console.error(error); // uncomment this if you want the user to see which avatars failed to load.
resolve();
});
});
};
const loadAvatar = (identity: string) => {
// fetches avatar from keybase and stores it in localStorage
fetchAvatar(identity).then(() => {
localStorage.setItem('avatars', JSON.stringify(avatars.value));
});
};
const loadAvatars = () => { const loadAvatars = () => {
// fetch avatar from keybase // fetches all avatars from keybase and stores it in localStorage
let promise = Promise.resolve(); const promises = staking.validators.map((validator) => {
staking.validators.forEach((item) => { const identity = validator.description?.identity;
promise = promise.then(
() => // Here we also check whether we haven't already fetched the avatar
new Promise((resolve) => { if (identity && !avatars.value[identity]) {
const identity = item.description?.identity; return fetchAvatar(identity);
if (identity && !avatars.value[identity]) { } else {
staking.keybase(identity).then((d) => { return Promise.resolve();
if (Array.isArray(d.them) && d.them.length > 0) { }
const uri = String( });
d.them[0]?.pictures?.primary?.url
).replace( Promise.all(promises).then(() =>
'https://s3.amazonaws.com/keybase_processed_uploads/', localStorage.setItem('avatars', JSON.stringify(avatars.value))
'' );
);
if (uri) {
avatars.value[identity] = uri;
localStorage.setItem(
'avatars',
JSON.stringify(avatars.value)
);
}
}
resolve();
});
} else {
resolve();
}
})
);
});
}; };
const logo = (identity?: string) => { const logo = (identity?: string) => {
@ -313,7 +326,7 @@ loadAvatars();
style="max-width: 300px" style="max-width: 300px"
> >
<div <div
class="avatar mr-4 relative w-8 h-8 rounded-full overflow-hidden" class="avatar mr-4 relative w-8 h-8 rounded-full"
> >
<div <div
class="w-8 h-8 rounded-full bg-gray-400 absolute opacity-10" class="w-8 h-8 rounded-full bg-gray-400 absolute opacity-10"
@ -323,10 +336,16 @@ loadAvatars();
v-if="logo" v-if="logo"
:src="logo" :src="logo"
class="object-contain" class="object-contain"
@error="
(e) => {
const identity = v.description?.identity;
if (identity) loadAvatar(identity);
}
"
/> />
<Icon <Icon
v-else v-else
class="text-4xl" class="text-3xl"
:icon="`mdi-help-circle-outline`" :icon="`mdi-help-circle-outline`"
/> />