@prefecthq/prefect-ui-library
Version:
This library is the Vue and Typescript component library for [Prefect 2](https://github.com/PrefectHQ/prefect) and [Prefect Cloud 2](https://www.prefect.io/cloud/). _The components and utilities in this project are not meant to be used independently_.
102 lines (82 loc) • 3.33 kB
text/typescript
import { useSubscription, ActionArguments, ActionResponse, SubscribeArguments, UseSubscription, unrefArgs, watchableArgs } from '@prefecthq/vue-compositions'
import { computed, getCurrentInstance, onUnmounted, reactive, ref, watch } from 'vue'
import { uniqueValueWatcher } from '@/utilities/reactivity'
export type Paginated = { limit?: number, offset?: number }
// any is correct here
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type PaginatedAction = (filters: Paginated) => Promise<any[]>
export type UsePaginatedSubscription<T extends PaginatedAction> = {
loadMore: () => void,
} & Omit<UseSubscription<T>, 'promise'>
/**
* @deprecated Use dedicated compositions such as useFlowRunInfiniteScroll instead
*/
export function usePaginatedSubscription<T extends PaginatedAction>(...[action, args, options = {}]: SubscribeArguments<T>): UsePaginatedSubscription<T> {
const subscriptions = reactive<UseSubscription<T>[]>([])
const argsWithDefault = args ?? [] as unknown as ActionArguments<T>
const pages = ref(0)
const watchable = watchableArgs(argsWithDefault)
let unwatch: ReturnType<typeof watch> | undefined
const loading = computed(() => subscriptions.some(subscription => subscription.loading))
const errored = computed(() => subscriptions.some(subscription => subscription.errored))
const error = computed(() => subscriptions.length ? subscriptions[0].error : undefined)
const executed = computed(() => subscriptions.some(subscription => subscription.executed))
const response = computed(() => subscriptions.flatMap(subscription => subscription.response ?? []) as ActionResponse<T>)
const paused = computed(() => subscriptions.some(subscription => subscription.paused))
const late = computed(() => subscriptions.some(subscription => subscription.late))
const unsubscribe = (): void => {
subscriptions.forEach(subscription => subscription.unsubscribe())
}
const refresh = async (): Promise<void> => {
const promises = subscriptions.map(subscription => subscription.refresh())
await Promise.all(promises)
}
const isSubscribed = (): boolean => {
return subscriptions.every(subscription => subscription.isSubscribed())
}
const loadMore = (): void => {
const [unwrappedFilters] = unrefArgs(argsWithDefault)
const limit = unwrappedFilters.limit ?? 200
if (subscriptions.length * limit > response.value.length) {
return
}
const offset = (unwrappedFilters.offset ?? limit) * pages.value
const subscriptionFilters = [{ ...unwrappedFilters, offset, limit }] as Parameters<T>
const subscription = useSubscription<T>(action, subscriptionFilters, options)
subscriptions.push(reactive(subscription))
pages.value++
}
if (watchable !== null) {
unwatch = uniqueValueWatcher(watchable, () => {
if (!isSubscribed()) {
unwatch!()
return
}
pages.value = 0
unsubscribe()
subscriptions.splice(0)
loadMore()
}, { deep: true })
}
if (getCurrentInstance()) {
onUnmounted(() => {
unsubscribe()
if (unwatch) {
unwatch()
}
})
}
return reactive({
loading,
executed,
errored,
error,
response,
paused,
late,
unsubscribe,
refresh,
isSubscribed,
loadMore,
})
}