Skip to content

Commit

Permalink
Merge pull request #16914 from jmchilton/infinite_scrolling
Browse files Browse the repository at this point in the history
Fix GQL infinite scrolling in tool shed.
  • Loading branch information
mvdbeek authored Oct 25, 2023
2 parents 7ee3412 + 4b49b83 commit 4fcf3b5
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 79 deletions.
12 changes: 11 additions & 1 deletion lib/tool_shed/webapp/frontend/src/apollo.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
import { createApolloProvider } from "@vue/apollo-option"
import { ApolloClient, InMemoryCache, DefaultOptions } from "@apollo/client/core"

import { loadErrorMessages, loadDevMessages } from "@apollo/client/dev"

const dev = false
if (dev) {
// Adds messages only in a dev environment
loadDevMessages()
loadErrorMessages()
}

const defaultOptions: DefaultOptions = {
watchQuery: {
fetchPolicy: "no-cache",
Expand All @@ -12,9 +21,10 @@ const defaultOptions: DefaultOptions = {
},
}

export const cache = new InMemoryCache()
export const apolloClient = new ApolloClient({
uri: "/api/graphql/",
cache: new InMemoryCache(),
cache: cache,
defaultOptions: defaultOptions,
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import { nodeToRow } from "@/components/RepositoriesGridInterface"
import ErrorBanner from "@/components/ErrorBanner.vue"
import LoadingDiv from "@/components/LoadingDiv.vue"
import { graphql } from "@/gql"
import { useQuery } from "@vue/apollo-composable"
import type { Query, RelayRepositoryConnection } from "@/gql/graphql"
import { useRelayInfiniteScrollQuery } from "@/relayClient"
interface RepositoriesByOwnerProps {
username: string
Expand All @@ -30,34 +31,18 @@ const query = graphql(/* GraphQL */ `
}
`)
async function onScroll(): Promise<void> {
const cursor = result.value?.relayRepositoriesForOwner?.pageInfo.endCursor || null
fetchMore({
variables: {
username: props.username,
cursor: cursor,
},
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
updateQuery: (previousResult, { fetchMoreResult }) => {
const newRepos = fetchMoreResult?.relayRepositoriesForOwner?.edges || []
const edges = [...(previousResult?.relayRepositoriesForOwner?.edges || []), ...newRepos]
const pageInfo = { ...fetchMoreResult?.relayRepositoriesForOwner?.pageInfo }
return {
relayRepositoriesForOwner: {
__typename: fetchMoreResult?.relayRepositoriesForOwner?.__typename,
// Merging the tag list
edges: edges,
pageInfo: pageInfo,
},
}
},
})
function toResult(queryResult: Query): RelayRepositoryConnection {
const result = queryResult.relayRepositoriesForOwner
if (!result) {
throw Error("problem")
}
return result as RelayRepositoryConnection
}
const { result, loading, error, fetchMore } = useQuery(query, { username: props.username })
const { records, error, loading, onScroll } = useRelayInfiniteScrollQuery(query, { username: props.username }, toResult)
const rows = computed(() => {
const nodes = result.value?.relayRepositoriesForOwner?.edges.map((v) => v?.node)
return nodes?.map(nodeToRow) || []
return records.value.map(nodeToRow)
})
</script>
<template>
Expand Down
25 changes: 2 additions & 23 deletions lib/tool_shed/webapp/frontend/src/components/RepositoriesGrid.vue
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ const columns = computed(() => {
const tableLoading = ref(false)
async function onScroll(details: ScrollDetails) {
async function onVirtualScroll(details: ScrollDetails) {
console.log(details)
const { to, direction } = details
if (direction == "decrease") {
Expand Down Expand Up @@ -95,7 +95,7 @@ const adaptedRows = computed(() =>
:pagination="pagination"
:rows-per-page-options="[0]"
:no-data-label="noDataLabel"
@virtual-scroll="onScroll"
@virtual-scroll="onVirtualScroll"
hide-header
hide-bottom
>
Expand All @@ -122,27 +122,6 @@ const adaptedRows = computed(() =>
</q-tr>
</template>
</q-table>
<!--
<q-table
v-if="loading || rows.length > 0"
style="height: 90vh"
:title="title"
:rows="rows"
:columns="columns"
:loading="tableLoading"
row-key="index"
virtual-scroll
:virtual-scroll-item-size="48"
:virtual-scroll-sticky-size-start="48"
:pagination="pagination"
:rows-per-page-options="[0]"
:no-data-label="noDataLabel"
@virtual-scroll="onScroll"
hide-header
hide-bottom
>
</q-table>
-->
<q-banner rounded class="bg-warning text-white" v-else>
<!-- the no-data-label doesn't seem to be working,
probably because we're overriding the whole body
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { nodeToRow } from "@/components/RepositoriesGridInterface"
import ErrorBanner from "@/components/ErrorBanner.vue"
import LoadingDiv from "@/components/LoadingDiv.vue"
import { graphql } from "@/gql"
import { useQuery } from "@vue/apollo-composable"
import type { Query, RelayRepositoryConnection } from "@/gql/graphql"
import { useRelayInfiniteScrollQuery } from "@/relayClient"
const categoriesStore = useCategoriesStore()
const props = defineProps({
Expand Down Expand Up @@ -45,35 +45,22 @@ const query = graphql(/* GraphQL */ `
}
`)
async function onScroll(): Promise<void> {
const cursor = result.value?.relayRepositoriesForCategory?.pageInfo.endCursor || null
fetchMore({
variables: {
categoryId: props.categoryId,
cursor: cursor,
},
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
updateQuery: (previousResult, { fetchMoreResult }) => {
const newRepos = fetchMoreResult?.relayRepositoriesForCategory?.edges || []
const edges = [...(previousResult?.relayRepositoriesForCategory?.edges || []), ...newRepos]
const pageInfo = { ...fetchMoreResult?.relayRepositoriesForCategory?.pageInfo }
return {
relayRepositoriesForCategory: {
__typename: fetchMoreResult?.relayRepositoriesForCategory?.__typename,
// Merging the tag list
edges: edges,
pageInfo: pageInfo,
},
}
},
})
function toResult(queryResult: Query): RelayRepositoryConnection {
const result = queryResult.relayRepositoriesForCategory
if (!result) {
throw Error("problem")
}
return result as RelayRepositoryConnection
}
const { result, loading, error, fetchMore } = useQuery(query, { categoryId: props.categoryId })
const { records, error, loading, onScroll } = useRelayInfiniteScrollQuery(
query,
{ categoryId: props.categoryId },
toResult
)
const rows = computed(() => {
const nodes = result.value?.relayRepositoriesForCategory?.edges.map((v) => v?.node)
return nodes?.map(nodeToRow) || []
return records.value.map(nodeToRow)
})
</script>
<template>
Expand Down
63 changes: 63 additions & 0 deletions lib/tool_shed/webapp/frontend/src/relayClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { ref, type Ref } from "vue"
import type { OperationVariables } from "@apollo/client/core/index.js"
import { useLazyQuery } from "@vue/apollo-composable"
import { DocumentParameter } from "@vue/apollo-composable/dist/useQuery"
import type { Maybe, PageInfo } from "@/gql/graphql"

export interface InterfaceHasNode<NodeType> {
node?: Maybe<NodeType>
}

export interface RelayQuery<NodeType> {
pageInfo: PageInfo
edges: Array<Maybe<InterfaceHasNode<NodeType>>>
}

/* eslint-disable @typescript-eslint/no-explicit-any */
export function useRelayInfiniteScrollQuery<ResultType, NodeType>(
query: DocumentParameter<any, OperationVariables>,
variables: OperationVariables,
toResult: (arg: ResultType) => RelayQuery<NodeType>
) {
const records = ref<NodeType[]>([]) as Ref<Array<NodeType>>
const lastResult = ref<ResultType | null>(null) as Ref<ResultType | null>
const hasNextPage = ref(true)

const { load, error, loading, fetchMore } = useLazyQuery(query, variables, {
fetchPolicy: "network-only",
nextFetchPolicy: "network-only",
})

function handleResult(result: ResultType) {
lastResult.value = result
const asResult: RelayQuery<NodeType> = toResult(result)
hasNextPage.value = asResult.pageInfo.hasNextPage
const nodes: NodeType[] = asResult.edges.flatMap((x) => (x && x.node ? [x.node] : []))
records.value.push(...nodes)
}

async function onScroll() {
if (hasNextPage.value && lastResult.value != null && fetchMore != null) {
const asResult: RelayQuery<NodeType> = toResult(lastResult.value)
const endCursor = asResult.pageInfo.endCursor
const fetchMoreVariables = { ...variables, cursor: endCursor }
const fetchMorePromise = fetchMore({
variables: fetchMoreVariables,
/*
fetchPolicy: "network-only",
nextFetchPolicy: "network-only",
*/
})
if (fetchMorePromise) {
fetchMorePromise.then((result) => handleResult(result.data as ResultType))
}
}
}

const loadPromise = load()
if (loadPromise) {
loadPromise.then(handleResult)
}

return { records, error, loading, onScroll }
}

0 comments on commit 4fcf3b5

Please sign in to comment.