Skip to content

Commit

Permalink
feat: add statements to delegates list (#641)
Browse files Browse the repository at this point in the history
* feat: add statement to delegates

* feat: load and attach statements to delegates list

* feat: add dropdown menu to delegate

* fix: link menu to user space statement

* fix: update graphql query to use more loose variables

* feat: add link to block explorer

* fix: make addresses comparison case-insensitive

* feat: delegate button open delegate modal with prefilled address

* refactor: better function name

* fix: enable `network` filter

* fix: use metadataNetwork instead of first available offchain network

* fix: improve column width

* fix(ux): expand link to whole line

* refactor: trigger both async request in parallel

* fix: strip markdown from statement excerpt

* fix: fix nested links

* fix: use single link

* fix: set value before modal open

* fix(ui): add fallback for browser not supporting line clamp css

* fix: use tailwind class to clamp line

* fix: increase text size and ensure truncated text always end up with ...
  • Loading branch information
wa0x6e authored Aug 26, 2024
1 parent 69ae064 commit 26a108f
Show file tree
Hide file tree
Showing 10 changed files with 205 additions and 52 deletions.
7 changes: 4 additions & 3 deletions apps/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,6 @@
"@apollo/client": "^3.9.5",
"@braintree/sanitize-url": "^6.0.4",
"@ensdomains/eth-ens-namehash": "^2.0.15",
"@ethersproject/keccak256": "^5.7.0",
"@ethersproject/solidity": "^5.7.0",
"@ethersproject/strings": "^5.7.0",
"@ethersproject/abi": "^5.7.0",
"@ethersproject/abstract-signer": "^5.7.0",
"@ethersproject/address": "^5.7.0",
Expand All @@ -39,7 +36,10 @@
"@ethersproject/constants": "^5.7.0",
"@ethersproject/contracts": "^5.7.0",
"@ethersproject/hash": "^5.7.0",
"@ethersproject/keccak256": "^5.7.0",
"@ethersproject/providers": "^5.7.2",
"@ethersproject/solidity": "^5.7.0",
"@ethersproject/strings": "^5.7.0",
"@ethersproject/units": "^5.7.0",
"@ethersproject/wallet": "^5.7.0",
"@headlessui/vue": "^1.7.16",
Expand Down Expand Up @@ -73,6 +73,7 @@
"object-hash": "^3.0.0",
"pinia": "^2.1.6",
"remarkable": "^2.0.1",
"remove-markdown": "^0.5.2",
"scrollmonitor": "^1.2.11",
"starknet": "6.11.0",
"starknetkit": "^1.1.9",
Expand Down
174 changes: 133 additions & 41 deletions apps/ui/src/components/SpaceDelegates.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<script setup lang="ts">
import removeMarkdown from 'remove-markdown';
import { _n, _p, shorten } from '@/helpers/utils';
import { getNetwork } from '@/networks';
import { Space, SpaceMetadataDelegation } from '@/types';
Expand All @@ -9,6 +10,7 @@ const props = defineProps<{
}>();
const delegateModalOpen = ref(false);
const delegateModalState = ref<{ delegatee: string } | null>(null);
const sortBy = ref(
'delegatedVotes-desc' as
| 'delegatedVotes-desc'
Expand All @@ -29,7 +31,8 @@ const {
reset
} = useDelegates(
props.delegation.apiUrl as string,
props.delegation.contractAddress as string
props.delegation.contractAddress as string,
props.space
);
const currentNetwork = computed(() => {
Expand Down Expand Up @@ -60,6 +63,11 @@ async function handleEndReached() {
await fetchMore(sortBy.value);
}
function handleDelegateClick(delegatee?: string) {
delegateModalState.value = delegatee ? { delegatee } : null;
delegateModalOpen.value = true;
}
onMounted(() => {
if (!props.delegation.apiUrl) return;
Expand All @@ -86,23 +94,30 @@ watchEffect(() => setTitle(`Delegates - ${props.space.name}`));
<div v-if="delegation.contractAddress" class="p-4 space-x-2 flex">
<div class="flex-auto" />
<UiTooltip title="Delegate">
<UiButton class="!px-0 w-[46px]" @click="delegateModalOpen = true">
<UiButton class="!px-0 w-[46px]" @click="handleDelegateClick()">
<IH-user-add class="inline-block" />
</UiButton>
</UiTooltip>
</div>
<UiLabel label="Delegates" sticky />
<div class="text-left table-fixed w-full">
<div
class="bg-skin-bg border-b sticky top-[112px] lg:top-[113px] z-40 flex w-full font-medium space-x-1"
class="bg-skin-bg border-b sticky top-[112px] lg:top-[113px] z-40 flex w-full font-medium space-x-3 px-4"
>
<div class="pl-4 w-[60%] flex items-center truncate">Delegatee</div>
<div
class="w-[190px] grow sm:grow-0 sm:shrink-0 flex items-center truncate"
>
<span class="truncate">Delegatee</span>
</div>
<div class="hidden sm:flex grow items-center truncate">
<span class="truncate">Statement</span>
</div>
<button
type="button"
class="hidden md:flex w-[20%] items-center justify-end hover:text-skin-link space-x-1 truncate"
class="hidden md:flex w-[80px] shrink-0 items-center justify-end hover:text-skin-link space-x-1 truncate"
@click="handleSortChange('tokenHoldersRepresentedAmount')"
>
<span>Delegators</span>
<span class="truncate">Delegators</span>
<IH-arrow-sm-down
v-if="sortBy === 'tokenHoldersRepresentedAmount-desc'"
class="shrink-0"
Expand All @@ -114,7 +129,7 @@ watchEffect(() => setTitle(`Delegates - ${props.space.name}`));
</button>
<button
type="button"
class="w-[40%] md:w-[20%] flex justify-end items-center hover:text-skin-link pr-4 space-x-1 truncate"
class="w-[150px] flex sm:shrink-0 justify-end items-center hover:text-skin-link space-x-1 truncate"
@click="handleSortChange('delegatedVotes')"
>
<span class="truncate">Voting power</span>
Expand All @@ -127,6 +142,7 @@ watchEffect(() => setTitle(`Delegates - ${props.space.name}`));
class="shrink-0"
/>
</button>
<div class="w-[20px]" />
</div>
<UiLoading v-if="loading" class="px-4 py-3 block" />
<template v-else>
Expand All @@ -145,47 +161,122 @@ watchEffect(() => setTitle(`Delegates - ${props.space.name}`));
<div
v-for="(delegate, i) in delegates"
:key="i"
class="border-b flex space-x-1"
class="border-b flex space-x-3 px-4"
>
<div class="flex items-center w-[60%] pl-4 py-3 gap-x-3 truncate">
<UiStamp :id="delegate.user" :size="32" />
<router-link
:to="{
name: 'space-user-statement',
params: {
id: `${space.network}:${space.id}`,
user: delegate.user
}
}"
class="overflow-hidden leading-[22px]"
<router-link
:to="{
name: 'space-user-statement',
params: {
id: `${space.network}:${space.id}`,
user: delegate.user
}
}"
class="flex w-full"
>
<div
class="flex grow sm:grow-0 sm:shrink-0 items-center w-[190px] py-3 gap-x-3 leading-[22px] truncate"
>
<UiStamp :id="delegate.user" :size="32" />
<div class="flex flex-col truncate">
<h4
class="text-skin-link truncate"
v-text="delegate.name || shorten(delegate.user)"
/>
<div
class="text-[17px] text-skin-text truncate"
v-text="shorten(delegate.user)"
/>
</div>
</div>
<div
class="hidden sm:flex items-center grow text-[17px] overflow-hidden leading-[22px] text-skin-heading"
>
<div
v-if="delegate.statement"
class="line-clamp-2 max-h-[44px]"
v-text="
shorten(removeMarkdown(delegate.statement.statement), 250)
"
/>
</div>
<div
class="hidden md:flex shrink-0 w-[80px] flex-col items-end justify-center leading-[22px] truncate"
>
<h4
class="text-skin-link truncate"
v-text="delegate.name || shorten(delegate.user)"
class="text-skin-link"
v-text="_n(delegate.tokenHoldersRepresentedAmount)"
/>
<div
class="text-[17px] text-skin-text truncate"
v-text="shorten(delegate.user)"
class="text-[17px]"
v-text="_p(delegate.delegatorsPercentage)"
/>
</router-link>
</div>
<div
class="hidden md:flex w-[20%] flex-col items-end justify-center leading-[22px] truncate"
>
<h4
class="text-skin-link"
v-text="_n(delegate.tokenHoldersRepresentedAmount)"
/>
</div>
<div
class="text-[17px]"
v-text="_p(delegate.delegatorsPercentage)"
/>
</div>
<div
class="w-[40%] md:w-[20%] flex flex-col items-end justify-center pr-4 leading-[22px] truncate"
>
<h4 class="text-skin-link" v-text="_n(delegate.delegatedVotes)" />
<div class="text-[17px]" v-text="_p(delegate.votesPercentage)" />
class="w-[150px] flex flex-col sm:shrink-0 items-end justify-center leading-[22px] truncate"
>
<h4
class="text-skin-link"
v-text="_n(delegate.delegatedVotes)"
/>
<div
class="text-[17px]"
v-text="_p(delegate.votesPercentage)"
/>
</div>
</router-link>
<div class="flex items-center justify-center">
<UiDropdown>
<template #button>
<UiButton class="!p-0 border-0 !h-[auto] bg-transparent">
<IH-dots-horizontal class="text-skin-link" />
</UiButton>
</template>
<template #items>
<UiDropdownItem v-slot="{ active }">
<button
type="button"
class="flex items-center gap-2"
:class="{ 'opacity-80': active }"
@click="handleDelegateClick(delegate.user)"
>
<IH-user-add />
Delegate
</button>
</UiDropdownItem>
<UiDropdownItem v-slot="{ active }">
<router-link
:to="{
name: 'space-user-statement',
params: {
id: `${space.network}:${space.id}`,
user: delegate.user
}
}"
class="flex items-center gap-2"
:class="{ 'opacity-80': active }"
>
<IH-user-circle />
View profile
</router-link>
</UiDropdownItem>
<UiDropdownItem v-slot="{ active }">
<a
:href="
currentNetwork.helpers.getExplorerUrl(
delegate.user,
'address'
)
"
target="_blank"
class="flex items-center gap-2"
:class="{ 'opacity-80': active }"
>
<IH-arrow-sm-right class="-rotate-45" />
View on block explorer
</a>
</UiDropdownItem>
</template>
</UiDropdown>
</div>
</div>
<template #loading>
Expand All @@ -200,6 +291,7 @@ watchEffect(() => setTitle(`Delegates - ${props.space.name}`));
:open="delegateModalOpen"
:space="space"
:delegation="delegation"
:initial-state="delegateModalState"
@close="delegateModalOpen = false"
/>
</teleport>
Expand Down
27 changes: 24 additions & 3 deletions apps/ui/src/composables/useDelegates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import {
} from '@apollo/client/core';
import gql from 'graphql-tag';
import { getNames } from '@/helpers/stamp';
import { getNetwork, metadataNetwork as metadataNetworkId } from '@/networks';
import { Space, Statement } from '@/types';

type ApiDelegate = {
id: string;
Expand All @@ -18,6 +20,7 @@ export type Delegate = ApiDelegate & {
name: string | null;
delegatorsPercentage: number;
votesPercentage: number;
statement?: Statement;
};

type Governance = {
Expand Down Expand Up @@ -70,6 +73,8 @@ const DELEGATES_QUERY = gql`
}
`;

const metadataNetwork = getNetwork(metadataNetworkId);

function convertUrl(apiUrl: string) {
const hostedPattern =
/https:\/\/thegraph\.com\/hosted-service\/subgraph\/([\w-]+)\/([\w-]+)/;
Expand All @@ -82,7 +87,11 @@ function convertUrl(apiUrl: string) {
return apiUrl;
}

export function useDelegates(delegationApiUrl: string, governance: string) {
export function useDelegates(
delegationApiUrl: string,
governance: string,
space: Space
) {
const delegates: Ref<Delegate[]> = ref([]);
const loading = ref(false);
const loadingMore = ref(false);
Expand Down Expand Up @@ -113,7 +122,18 @@ export function useDelegates(delegationApiUrl: string, governance: string) {
const governanceData = data.governance;
const delegatesData = data.delegates;
const addresses = delegatesData.map(delegate => delegate.user);
const names = await getNames(addresses);

const [names, statements] = await Promise.all([
getNames(addresses),
metadataNetwork.api.loadStatements(space.network, space.id, addresses)
]);
const indexedStatements = statements.reduce(
(acc, statement) => {
acc[statement.delegate.toLowerCase()] = statement;
return acc;
},
{} as Record<Statement['delegate'], Statement>
);

return delegatesData.map((delegate: ApiDelegate) => {
const delegatorsPercentage =
Expand All @@ -127,7 +147,8 @@ export function useDelegates(delegationApiUrl: string, governance: string) {
name: names[delegate.user] || null,
...delegate,
delegatorsPercentage,
votesPercentage
votesPercentage,
statement: indexedStatements[delegate.user.toLowerCase()]
};
});
}
Expand Down
3 changes: 3 additions & 0 deletions apps/ui/src/networks/common/graphqlApi/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,9 @@ export function createApi(
},
loadStatement: async () => {
return null;
},
loadStatements: async () => {
return [];
}
};
}
Loading

0 comments on commit 26a108f

Please sign in to comment.