Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: fetch space delegates using vue-query #1125

Merged
merged 2 commits into from
Jan 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"@snapshot-labs/prettier-config": "^0.1.0-beta.18",
"@snapshot-labs/snapshot.js": "^0.12.43",
"@snapshot-labs/sx": "^0.1.0",
"@tanstack/vue-query": "^5.64.2",
"@vueuse/core": "^10.4.1",
"@walletconnect/core": "^2.11.0",
"@walletconnect/types": "^2.11.0",
Expand Down
84 changes: 45 additions & 39 deletions apps/ui/src/components/SpaceDelegates.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<script setup lang="ts">
import { sanitizeUrl } from '@braintree/sanitize-url';
import { useInfiniteQuery } from '@tanstack/vue-query';
import removeMarkdown from 'remove-markdown';
import { getGenericExplorerUrl } from '@/helpers/explorer';
import { _n, _p, _vp, shorten } from '@/helpers/utils';
Expand All @@ -21,18 +22,7 @@ const sortBy = ref(
| 'tokenHoldersRepresentedAmount-asc'
);
const { setTitle } = useTitle();
const {
loading,
loadingMore,
loaded,
failed,
errorCode,
hasMore,
delegates,
fetch,
fetchMore,
reset
} = useDelegates(
const { getDelegates } = useDelegates(
props.delegation.apiType as DelegationType,
props.delegation.apiUrl as string,
props.delegation.contractAddress as string,
Expand All @@ -42,6 +32,39 @@ const { web3 } = useWeb3();

const spaceKey = computed(() => `${props.space.network}:${props.space.id}`);

const {
data,
error,
fetchNextPage,
hasNextPage,
isPending,
isFetchingNextPage,
isError
} = useInfiniteQuery({
initialPageParam: 0,
queryKey: ['delegates', props.delegation.contractAddress, sortBy],
queryFn: ({ pageParam }) => {
const [orderBy, orderDirection] = sortBy.value.split('-');

return getDelegates({
orderBy,
orderDirection,
first: 40,
skip: pageParam
});
},
getNextPageParam: (lastPage, pages) => {
if (lastPage.length < 40) return null;

return pages.length * 40;
},
retry: (failureCount, error) => {
if (error?.message.includes('Row not found')) return false;

return failureCount < 3;
}
});

function getExplorerUrl(address: string, type: 'address' | 'token') {
let url: string | null = null;
if (props.delegation.chainId) {
Expand All @@ -67,28 +90,11 @@ function handleSortChange(
}
}

async function handleEndReached() {
if (!hasMore.value) return;

await fetchMore(sortBy.value);
}

function handleDelegateClick(delegatee?: string) {
delegateModalState.value = delegatee ? { delegatee } : null;
delegateModalOpen.value = true;
}

onMounted(() => {
if (!props.delegation.apiUrl) return;

fetch(sortBy.value);
});

watch([sortBy], () => {
reset();
fetch(sortBy.value);
});

watchEffect(() => setTitle(`Delegates - ${props.space.name}`));
</script>

Expand Down Expand Up @@ -182,27 +188,27 @@ watchEffect(() => setTitle(`Delegates - ${props.space.name}`));
</button>
<div class="w-[20px]" />
</div>
<UiLoading v-if="loading" class="px-4 py-3 block" />
<UiLoading v-if="isPending" class="px-4 py-3 block" />
<template v-else>
<div
v-if="loaded && (delegates.length === 0 || failed)"
v-if="data?.pages.length === 0 || isError"
class="px-4 py-3 flex items-center space-x-1"
>
<IH-exclamation-circle class="shrink-0" />
<span v-if="errorCode === 'initializing'">
<span v-if="error?.message.includes('Row not found')">
Delegates are being computed, please come back later.
</span>
<span v-else-if="failed">Failed to load delegates.</span>
<span v-else-if="delegates.length === 0">
There are no delegates.
</span>
<span v-else-if="isError">Failed to load delegates.</span>
<span v-else-if="data?.pages.length === 0">
There are no delegates.</span
>
</div>
<UiContainerInfiniteScroll
:loading-more="loadingMore"
@end-reached="handleEndReached"
:loading-more="isFetchingNextPage"
@end-reached="hasNextPage && fetchNextPage()"
>
<div
v-for="(delegate, i) in delegates"
v-for="(delegate, i) in data?.pages.flat()"
:key="i"
class="border-b flex space-x-3 px-4"
>
Expand Down
85 changes: 1 addition & 84 deletions apps/ui/src/composables/useDelegates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,6 @@ type DelegatesQueryFilter = {
first: number;
};

type SortOrder =
| 'delegatedVotes-desc'
| 'delegatedVotes-asc'
| 'tokenHoldersRepresentedAmount-desc'
| 'tokenHoldersRepresentedAmount-asc';

const DEFAULT_ORDER = 'delegatedVotes-desc';

const DELEGATES_LIMIT = 40;

const DELEGATES_QUERY = gql`
query (
$first: Int!
Expand Down Expand Up @@ -93,14 +83,6 @@ export function useDelegates(
governance: string,
space: Space
) {
const delegates: Ref<Delegate[]> = ref([]);
const loading = ref(false);
const loadingMore = ref(false);
const loaded = ref(false);
const failed = ref(false);
const hasMore = ref(false);
const errorCode = ref<'initializing' | null>(null);

const httpLink = createHttpLink({
uri: convertUrl(delegationApiUrl)
});
Expand Down Expand Up @@ -166,72 +148,7 @@ export function useDelegates(
return formatDelegates(data);
}

async function _fetch(overwrite: boolean, sortBy: SortOrder) {
const [orderBy, orderDirection] = sortBy.split('-');

const newDelegates = await getDelegates({
orderBy,
orderDirection,
skip: overwrite ? 0 : delegates.value.length,
first: DELEGATES_LIMIT
});

delegates.value = overwrite
? newDelegates
: [...delegates.value, ...newDelegates];

hasMore.value = newDelegates.length === DELEGATES_LIMIT;
}

async function fetch(sortBy: SortOrder = DEFAULT_ORDER) {
if (loading.value || loaded.value) return;
loading.value = true;

try {
await _fetch(true, sortBy);

loaded.value = true;
} catch (e) {
failed.value = true;

if (e.message.includes('Row not found')) {
errorCode.value = 'initializing';
}
} finally {
loading.value = false;
loaded.value = true;
}
}

async function fetchMore(sortBy: SortOrder = DEFAULT_ORDER) {
if (loading.value || !loaded.value) return;
loadingMore.value = true;

await _fetch(false, sortBy);

loadingMore.value = false;
}

function reset() {
delegates.value = [];
loading.value = false;
loadingMore.value = false;
loaded.value = false;
failed.value = false;
hasMore.value = false;
}

return {
loading,
loadingMore,
loaded,
failed,
errorCode,
hasMore,
delegates,
getDelegates,
fetch,
fetchMore,
reset
getDelegates
};
}
2 changes: 2 additions & 0 deletions apps/ui/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { LockPlugin } from '@snapshot-labs/lock/plugins/vue3';
import { VueQueryPlugin } from '@tanstack/vue-query';
import { createPinia } from 'pinia';
import VueTippy from 'vue-tippy';
import App from '@/App.vue';
Expand Down Expand Up @@ -45,6 +46,7 @@ const app = createApp({ render: () => h(App) })
});

app.use(pinia);
app.use(VueQueryPlugin);

app.mount('#app');

Expand Down
34 changes: 33 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2712,11 +2712,33 @@
dependencies:
defer-to-connect "^2.0.0"

"@tanstack/match-sorter-utils@^8.19.4":
version "8.19.4"
resolved "https://registry.yarnpkg.com/@tanstack/match-sorter-utils/-/match-sorter-utils-8.19.4.tgz#dacf772b5d94f4684f10dbeb2518cf72dccab8a5"
integrity sha512-Wo1iKt2b9OT7d+YGhvEPD3DXvPv2etTusIMhMUoG7fbhmxcXCtIjJDEygy91Y2JFlwGyjqiBPRozme7UD8hoqg==
dependencies:
remove-accents "0.5.0"

"@tanstack/[email protected]":
version "5.64.2"
resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.64.2.tgz#be06e7c7966d14ea3e7c82bea1086b463f2f6809"
integrity sha512-hdO8SZpWXoADNTWXV9We8CwTkXU88OVWRBcsiFrk7xJQnhm6WRlweDzMD+uH+GnuieTBVSML6xFa17C2cNV8+g==

"@tanstack/[email protected]":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@tanstack/virtual-core/-/virtual-core-3.0.0.tgz#637bee36f0cabf96a1d436887c90f138a7e9378b"
integrity sha512-SYXOBTjJb05rXa2vl55TTwO40A6wKu0R5i1qQwhJYNDIqaIGF7D0HsLw+pJAyi2OvntlEIVusx3xtbbgSUi6zg==

"@tanstack/vue-query@^5.64.2":
version "5.64.2"
resolved "https://registry.yarnpkg.com/@tanstack/vue-query/-/vue-query-5.64.2.tgz#d77c5fcd26dd4572fce9ae1cc3e86f6b055573ed"
integrity sha512-W5jvqLKK8VUeTqK1tQ1CF7fV8Z7w0oAxYiYHdcJyBRmhPVVHuSsgjIh1DX8uwbr8fTSQDt7iGboT7HqkIkPziA==
dependencies:
"@tanstack/match-sorter-utils" "^8.19.4"
"@tanstack/query-core" "5.64.2"
"@vue/devtools-api" "^6.6.3"
vue-demi "^0.14.10"

"@tanstack/vue-virtual@^3.0.0-beta.60":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@tanstack/vue-virtual/-/vue-virtual-3.0.4.tgz#e4a7dea0ecdbd87cc45169c2ba1d3fd508232145"
Expand Down Expand Up @@ -3401,6 +3423,11 @@
resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.5.1.tgz#7f71f31e40973eeee65b9a64382b13593fdbd697"
integrity sha512-+KpckaAQyfbvshdDW5xQylLni1asvNSGme1JFs8I1+/H5pHEhqUKMEQD/qn3Nx5+/nycBq11qAEi8lk+LXI2dA==

"@vue/devtools-api@^6.6.3":
version "6.6.4"
resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz#cbe97fe0162b365edc1dba80e173f90492535343"
integrity sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==

"@vue/[email protected]":
version "2.1.6"
resolved "https://registry.yarnpkg.com/@vue/language-core/-/language-core-2.1.6.tgz#b48186bdb9b3ef2b83e1f76d5b1ac357b3a7ed94"
Expand Down Expand Up @@ -11374,6 +11401,11 @@ remarkable@^2.0.1:
argparse "^1.0.10"
autolinker "^3.11.0"

[email protected]:
version "0.5.0"
resolved "https://registry.yarnpkg.com/remove-accents/-/remove-accents-0.5.0.tgz#77991f37ba212afba162e375b627631315bed687"
integrity sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==

remove-markdown@^0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/remove-markdown/-/remove-markdown-0.5.2.tgz#1e52602260cc1e65e6f0c4f9e9662141ef3bd302"
Expand Down Expand Up @@ -13434,7 +13466,7 @@ vue-component-type-helpers@^2.0.0:
resolved "https://registry.yarnpkg.com/vue-component-type-helpers/-/vue-component-type-helpers-2.0.10.tgz#4bca46356312450c4f228d043895dd256bad305c"
integrity sha512-FC5fKJjDks3Ue/KRSYBdsiCaZa0kUPQfs8yQpb8W9mlO6BenV8G1z58xobeRMzevnmEcDa09LLwuXDwb4f6NMQ==

vue-demi@>=0.13.0:
vue-demi@>=0.13.0, vue-demi@^0.14.10:
version "0.14.10"
resolved "https://registry.yarnpkg.com/vue-demi/-/vue-demi-0.14.10.tgz#afc78de3d6f9e11bf78c55e8510ee12814522f04"
integrity sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==
Expand Down