refactor: use tanstack query to refactor attachments-related fetching (#878)

#### What type of PR is this?

/kind improvement

#### What this PR does / why we need it:

使用 [TanStack Query](https://github.com/TanStack/query) 重构附件相关数据请求的相关逻辑。

#### Which issue(s) this PR fixes:

Ref https://github.com/halo-dev/halo/issues/3360

#### Special notes for your reviewer:

测试方式:

1. 测试附件管理页面的筛选、存储策略、分组等业务。
2. 测试附件选择模态框组件。

#### Does this PR introduce a user-facing change?

```release-note
None
```
pull/879/head
Ryan Wang 2023-02-23 17:08:12 +08:00 committed by GitHub
parent 43c5effcd0
commit 0f8e5fad80
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 198 additions and 333 deletions

View File

@ -339,7 +339,7 @@ const editor = useEditor({
});
// image drag and paste upload
const { policies } = useFetchAttachmentPolicy({ fetchOnMounted: true });
const { policies } = useFetchAttachmentPolicy();
type Task = {
file: File;
@ -349,7 +349,7 @@ type Task = {
const uploadQueue: queueAsPromised<Task> = fastq.promise(asyncWorker, 1);
async function asyncWorker(arg: Task): Promise<void> {
if (!policies.value.length) {
if (!policies.value?.length) {
Toast.warning("目前没有可用的存储策略");
return;
}

View File

@ -51,10 +51,8 @@ const policyVisible = ref(false);
const uploadVisible = ref(false);
const detailVisible = ref(false);
const { policies } = useFetchAttachmentPolicy({ fetchOnMounted: true });
const { groups, handleFetchGroups } = useFetchAttachmentGroup({
fetchOnMounted: true,
});
const { policies } = useFetchAttachmentPolicy();
const { groups, handleFetchGroups } = useFetchAttachmentGroup();
const selectedGroup = ref<Group>();
@ -85,26 +83,24 @@ const SortItems: SortItem[] = [
const selectedPolicy = ref<Policy>();
const selectedUser = ref<User>();
const keyword = ref<string>("");
const selectedSortItem = ref<SortItem>();
const selectedSortItemValue = computed(() => {
return selectedSortItem.value?.value;
});
function handleSelectPolicy(policy: Policy | undefined) {
selectedPolicy.value = policy;
handleFetchAttachments({ page: 1 });
page.value = 1;
}
function handleSelectUser(user: User | undefined) {
selectedUser.value = user;
handleFetchAttachments({ page: 1 });
page.value = 1;
}
function handleSortItemChange(sortItem?: SortItem) {
selectedSortItem.value = sortItem;
handleFetchAttachments({ page: 1 });
page.value = 1;
}
function handleKeywordChange() {
@ -112,12 +108,12 @@ function handleKeywordChange() {
if (keywordNode) {
keyword.value = keywordNode._value as string;
}
handleFetchAttachments({ page: 1 });
page.value = 1;
}
function handleClearKeyword() {
keyword.value = "";
handleFetchAttachments({ page: 1 });
page.value = 1;
}
const hasFilters = computed(() => {
@ -134,19 +130,24 @@ function handleClearFilters() {
selectedUser.value = undefined;
selectedSortItem.value = undefined;
keyword.value = "";
handleFetchAttachments({ page: 1 });
page.value = 1;
}
const keyword = ref<string>("");
const page = ref<number>(1);
const size = ref<number>(20);
const {
attachments,
selectedAttachment,
selectedAttachments,
checkedAll,
loading,
isLoading,
isFetching,
total,
handleFetchAttachments,
handleSelectNext,
handleSelectPrevious,
handlePaginationChange,
handleDelete,
handleDeleteInBatch,
handleCheckAll,
@ -159,6 +160,8 @@ const {
user: selectedUser,
keyword: keyword,
sort: selectedSortItemValue,
page: page,
size: size,
});
const handleMove = async (group: Group) => {
@ -209,21 +212,16 @@ const onDetailModalClose = () => {
selectedAttachment.value = undefined;
nameQuery.value = undefined;
nameQueryAttachment.value = undefined;
handleFetchAttachments({ mute: true });
handleFetchAttachments();
};
const onUploadModalClose = () => {
routeQueryAction.value = undefined;
handleFetchAttachments({ mute: true });
};
const onGroupChange = () => {
handleReset();
handleFetchAttachments();
};
const getPolicyName = (name: string | undefined) => {
const policy = policies.value.find((p) => p.metadata.name === name);
const policy = policies.value?.find((p) => p.metadata.name === name);
return policy?.spec.displayName;
};
@ -548,7 +546,7 @@ onMounted(() => {
>
<IconRefreshLine
v-tooltip="`刷新`"
:class="{ 'animate-spin text-gray-900': loading }"
:class="{ 'animate-spin text-gray-900': isFetching }"
class="h-4 w-4 text-gray-600 group-hover:text-gray-900"
/>
</div>
@ -562,15 +560,15 @@ onMounted(() => {
<div :style="`${viewType === 'list' ? 'padding:12px 16px 0' : ''}`">
<AttachmentGroupList
v-model:selected-group="selectedGroup"
@select="onGroupChange"
@select="handleReset"
@update="handleFetchGroups"
@reload-attachments="handleFetchAttachments"
/>
</div>
<VLoading v-if="loading" />
<VLoading v-if="isLoading" />
<Transition v-else-if="!attachments.total" appear name="fade">
<Transition v-else-if="!attachments?.length" appear name="fade">
<VEmpty
message="当前分组没有附件,你可以尝试刷新或者上传附件"
title="当前分组没有附件"
@ -600,7 +598,7 @@ onMounted(() => {
role="list"
>
<VCard
v-for="(attachment, index) in attachments.items"
v-for="(attachment, index) in attachments"
:key="index"
:body-class="['!p-0']"
:class="{
@ -680,10 +678,7 @@ onMounted(() => {
class="box-border h-full w-full divide-y divide-gray-100"
role="list"
>
<li
v-for="(attachment, index) in attachments.items"
:key="index"
>
<li v-for="(attachment, index) in attachments" :key="index">
<VEntity :is-selected="isChecked(attachment)">
<template
v-if="
@ -797,11 +792,10 @@ onMounted(() => {
<template #footer>
<div class="bg-white sm:flex sm:items-center sm:justify-end">
<VPagination
:page="attachments.page"
:size="attachments.size"
:total="attachments.total"
v-model:page="page"
v-model:size="size"
:total="total"
:size-options="[60, 120, 200]"
@change="handlePaginationChange"
/>
</div>
</template>

View File

@ -1,13 +1,14 @@
<script lang="ts" setup>
import { VButton, VModal, VSpace, VTag } from "@halo-dev/components";
import LazyImage from "@/components/image/LazyImage.vue";
import type { Attachment, Policy } from "@halo-dev/api-client";
import type { Attachment } from "@halo-dev/api-client";
import prettyBytes from "pretty-bytes";
import { ref, watch, watchEffect } from "vue";
import { computed, ref } from "vue";
import { apiClient } from "@/utils/api-client";
import { isImage } from "@/utils/image";
import { formatDatetime } from "@/utils/date";
import { useFetchAttachmentGroup } from "../composables/use-attachment-group";
import { useQuery } from "@tanstack/vue-query";
const props = withDefaults(
defineProps<{
@ -27,43 +28,40 @@ const emit = defineEmits<{
(event: "close"): void;
}>();
const { groups, handleFetchGroups } = useFetchAttachmentGroup();
const { groups } = useFetchAttachmentGroup();
const policy = ref<Policy>();
const onlyPreview = ref(false);
watchEffect(async () => {
if (props.attachment) {
const { policyName } = props.attachment.spec;
if (!policyName) {
return;
}
const { data } =
await apiClient.extension.storage.policy.getstorageHaloRunV1alpha1Policy({
name: policyName,
});
policy.value = data;
}
const policyName = computed(() => {
return props.attachment?.spec.policyName;
});
watch(
() => props.visible,
(newValue) => {
if (newValue) {
handleFetchGroups();
const { data: policy } = useQuery({
queryKey: ["attachment-policy", policyName],
queryFn: async () => {
if (!policyName.value) {
return;
}
}
);
const { data } =
await apiClient.extension.storage.policy.getstorageHaloRunV1alpha1Policy({
name: policyName.value,
});
return data;
},
refetchOnWindowFocus: false,
enabled: computed(() => !!policyName.value),
});
const getGroupName = (name: string | undefined) => {
const group = groups.value.find((group) => group.metadata.name === name);
const group = groups.value?.find((group) => group.metadata.name === name);
return group?.spec.displayName || name;
};
const onVisibleChange = (visible: boolean) => {
emit("update:visible", visible);
if (!visible) {
policy.value = undefined;
onlyPreview.value = false;
emit("close");
}

View File

@ -163,9 +163,9 @@ const handleDeleteWithAttachments = (group: Group) => {
};
watch(
() => groups.value.length,
() => groups.value?.length,
() => {
const allGroups = [...defaultGroups, ...groups.value];
const allGroups = [...defaultGroups, ...(groups.value || [])];
const groupIndex = allGroups.findIndex(
(group) => group.metadata.name === routeQuery.value
);
@ -178,9 +178,8 @@ watch(
onMounted(async () => {
await handleFetchGroups();
if (routeQuery.value && !props.readonly) {
const allGroups = [...defaultGroups, ...groups.value];
const allGroups = [...defaultGroups, ...(groups.value || [])];
const group = allGroups.find(
(group) => group.metadata.name === routeQuery.value
);

View File

@ -12,7 +12,7 @@ import {
Toast,
} from "@halo-dev/components";
import AttachmentPolicyEditingModal from "./AttachmentPolicyEditingModal.vue";
import { ref, watch } from "vue";
import { ref } from "vue";
import type { Policy, PolicyTemplate } from "@halo-dev/api-client";
import { formatDatetime } from "@/utils/date";
import {
@ -21,7 +21,7 @@ import {
} from "../composables/use-attachment-policy";
import { apiClient } from "@/utils/api-client";
const props = withDefaults(
withDefaults(
defineProps<{
visible: boolean;
}>(),
@ -35,11 +35,8 @@ const emit = defineEmits<{
(event: "close"): void;
}>();
const { policies, loading, handleFetchPolicies } = useFetchAttachmentPolicy();
const { policyTemplates, handleFetchPolicyTemplates } =
useFetchAttachmentPolicyTemplate({
fetchOnMounted: false,
});
const { policies, isLoading, handleFetchPolicies } = useFetchAttachmentPolicy();
const { policyTemplates } = useFetchAttachmentPolicyTemplate();
const selectedPolicy = ref<Policy>();
@ -105,16 +102,6 @@ const onEditingModalClose = () => {
selectedPolicy.value = undefined;
handleFetchPolicies();
};
watch(
() => props.visible,
(visible) => {
if (visible) {
handleFetchPolicyTemplates();
handleFetchPolicies();
}
}
);
</script>
<template>
<VModal
@ -150,7 +137,7 @@ watch(
</FloatingDropdown>
</template>
<VEmpty
v-if="!policies.length && !loading"
v-if="!policies?.length && !isLoading"
message="当前没有可用的存储策略,你可以尝试刷新或者新建策略"
title="当前没有可用的存储策略"
>

View File

@ -25,14 +25,9 @@ const emit = defineEmits<{
(event: "close"): void;
}>();
const { groups, handleFetchGroups } = useFetchAttachmentGroup({
fetchOnMounted: false,
});
const { policies, handleFetchPolicies } = useFetchAttachmentPolicy({
fetchOnMounted: false,
});
const { policyTemplates, handleFetchPolicyTemplates } =
useFetchAttachmentPolicyTemplate();
const { groups } = useFetchAttachmentGroup();
const { policies, handleFetchPolicies } = useFetchAttachmentPolicy();
const { policyTemplates } = useFetchAttachmentPolicyTemplate();
const selectedGroupName = useLocalStorage("attachment-upload-group", "");
const selectedPolicyName = useLocalStorage("attachment-upload-policy", "");
@ -45,12 +40,13 @@ watch(
() => {
if (selectedGroupName.value === "") return;
const group = groups.value.find(
const group = groups.value?.find(
(group) => group.metadata.name === selectedGroupName.value
);
if (!group) {
selectedGroupName.value =
groups.value.length > 0 ? groups.value[0].metadata.name : "";
selectedGroupName.value = groups.value?.length
? groups.value[0].metadata.name
: "";
}
}
);
@ -58,12 +54,13 @@ watch(
watch(
() => policies.value,
() => {
const policy = policies.value.find(
const policy = policies.value?.find(
(policy) => policy.metadata.name === selectedPolicyName.value
);
if (!policy) {
selectedPolicyName.value =
policies.value.length > 0 ? policies.value[0].metadata.name : "";
selectedPolicyName.value = policies.value?.length
? policies.value[0].metadata.name
: "";
}
}
);
@ -87,7 +84,7 @@ const handleOpenCreateNewPolicyModal = (policyTemplate: PolicyTemplate) => {
const onEditingModalClose = async () => {
await handleFetchPolicies();
policyToCreate.value = policies.value[0];
policyToCreate.value = policies.value?.[0];
};
const onVisibleChange = (visible: boolean) => {
@ -102,9 +99,6 @@ watch(
() => props.visible,
(newValue) => {
if (newValue) {
handleFetchGroups();
handleFetchPolicies();
handleFetchPolicyTemplates();
uploadVisible.value = true;
} else {
const uploadVisibleTimer = setTimeout(() => {
@ -131,7 +125,7 @@ watch(
<div
v-for="(group, index) in [
{ metadata: { name: '' }, spec: { displayName: '未分组' } },
...groups,
...(groups || []),
]"
:key="index"
:class="{
@ -200,7 +194,7 @@ watch(
</template>
</FloatingDropdown>
</div>
<div v-if="policies.length <= 0" class="mb-3">
<div v-if="!policies?.length" class="mb-3">
<VAlert
title="没有存储策略"
description="在上传之前,需要新建一个存储策略"

View File

@ -38,20 +38,22 @@ const emit = defineEmits<{
}>();
const selectedGroup = ref<Group>();
const page = ref(1);
const size = ref(60);
const {
attachments,
loading,
isLoading,
total,
selectedAttachment,
selectedAttachments,
handleFetchAttachments,
handlePaginationChange,
handleSelect,
handleSelectPrevious,
handleSelectNext,
handleReset,
isChecked,
} = useAttachmentControl({ group: selectedGroup });
} = useAttachmentControl({ group: selectedGroup, page, size });
const uploadVisible = ref(false);
const detailVisible = ref(false);
@ -64,21 +66,14 @@ const handleOpenDetail = (attachment: Attachment) => {
selectedAttachment.value = attachment;
detailVisible.value = true;
};
const onGroupChange = () => {
handleReset();
handleFetchAttachments();
};
await handleFetchAttachments();
</script>
<template>
<AttachmentGroupList
v-model:selected-group="selectedGroup"
readonly
@select="onGroupChange"
@select="handleReset"
/>
<div v-if="attachments.total > 0" class="mb-5">
<div v-if="attachments?.length" class="mb-5">
<VButton @click="uploadVisible = true">
<template #icon>
<IconUpload class="h-full w-full" />
@ -87,7 +82,7 @@ await handleFetchAttachments();
</VButton>
</div>
<VEmpty
v-if="!attachments.total && !loading"
v-if="!attachments?.length && !isLoading"
message="当前没有附件,你可以尝试刷新或者上传附件"
title="当前没有附件"
>
@ -109,7 +104,7 @@ await handleFetchAttachments();
role="list"
>
<VCard
v-for="(attachment, index) in attachments.items"
v-for="(attachment, index) in attachments"
:key="index"
:body-class="['!p-0']"
:class="{
@ -171,11 +166,10 @@ await handleFetchAttachments();
</div>
<div class="mt-4 bg-white sm:flex sm:items-center sm:justify-end">
<VPagination
:page="attachments.page"
:size="attachments.size"
:total="attachments.total"
v-model:page="page"
v-model:size="size"
:total="total"
:size-options="[60, 120, 200]"
@change="handlePaginationChange"
/>
</div>
<AttachmentUploadModal

View File

@ -1,58 +1,35 @@
import { onMounted, onUnmounted, ref, type Ref } from "vue";
import type { Ref } from "vue";
import type { Group } from "@halo-dev/api-client";
import { apiClient } from "@/utils/api-client";
import { useQuery } from "@tanstack/vue-query";
interface useFetchAttachmentGroupReturn {
groups: Ref<Group[]>;
loading: Ref<boolean>;
groups: Ref<Group[] | undefined>;
isLoading: Ref<boolean>;
handleFetchGroups: () => void;
}
export function useFetchAttachmentGroup(options?: {
fetchOnMounted: boolean;
}): useFetchAttachmentGroupReturn {
const { fetchOnMounted } = options || {};
const groups = ref<Group[]>([] as Group[]);
const loading = ref<boolean>(false);
const refreshInterval = ref();
const handleFetchGroups = async () => {
try {
clearInterval(refreshInterval.value);
loading.value = true;
export function useFetchAttachmentGroup(): useFetchAttachmentGroupReturn {
const { data, isLoading, refetch } = useQuery<Group[]>({
queryKey: ["attachment-groups"],
queryFn: async () => {
const { data } =
await apiClient.extension.storage.group.liststorageHaloRunV1alpha1Group();
groups.value = data.items;
const deletedGroups = groups.value.filter(
return data.items;
},
refetchInterval(data) {
const deletingGroups = data?.filter(
(group) => !!group.metadata.deletionTimestamp
);
if (deletedGroups.length) {
refreshInterval.value = setInterval(() => {
handleFetchGroups();
}, 1000);
}
} catch (e) {
console.error("Failed to fetch attachment groups", e);
} finally {
loading.value = false;
}
};
onMounted(() => {
fetchOnMounted && handleFetchGroups();
});
onUnmounted(() => {
clearInterval(refreshInterval.value);
return deletingGroups?.length ? 1000 : false;
},
refetchOnWindowFocus: false,
});
return {
groups,
loading,
handleFetchGroups,
groups: data,
isLoading,
handleFetchGroups: refetch,
};
}

View File

@ -1,97 +1,58 @@
import { onMounted, onUnmounted, ref } from "vue";
import type { Ref } from "vue";
import type { Policy, PolicyTemplate } from "@halo-dev/api-client";
import { apiClient } from "@/utils/api-client";
import { useQuery } from "@tanstack/vue-query";
interface useFetchAttachmentPolicyReturn {
policies: Ref<Policy[]>;
loading: Ref<boolean>;
policies: Ref<Policy[] | undefined>;
isLoading: Ref<boolean>;
handleFetchPolicies: () => void;
}
interface useFetchAttachmentPolicyTemplatesReturn {
policyTemplates: Ref<PolicyTemplate[]>;
loading: Ref<boolean>;
policyTemplates: Ref<PolicyTemplate[] | undefined>;
isLoading: Ref<boolean>;
handleFetchPolicyTemplates: () => void;
}
export function useFetchAttachmentPolicy(options?: {
fetchOnMounted: boolean;
}): useFetchAttachmentPolicyReturn {
const { fetchOnMounted } = options || {};
const policies = ref<Policy[]>([] as Policy[]);
const loading = ref<boolean>(false);
const refreshInterval = ref();
const handleFetchPolicies = async () => {
try {
clearInterval(refreshInterval.value);
loading.value = true;
export function useFetchAttachmentPolicy(): useFetchAttachmentPolicyReturn {
const { data, isLoading, refetch } = useQuery<Policy[]>({
queryKey: ["attachment-policies"],
queryFn: async () => {
const { data } =
await apiClient.extension.storage.policy.liststorageHaloRunV1alpha1Policy();
policies.value = data.items;
const deletedPolicies = policies.value.filter(
return data.items;
},
refetchInterval(data) {
const deletingPolicies = data?.filter(
(policy) => !!policy.metadata.deletionTimestamp
);
if (deletedPolicies.length) {
refreshInterval.value = setInterval(() => {
handleFetchPolicies();
}, 1000);
}
} catch (e) {
console.error("Failed to fetch attachment policies", e);
} finally {
loading.value = false;
}
};
onMounted(() => {
fetchOnMounted && handleFetchPolicies();
});
onUnmounted(() => {
clearInterval(refreshInterval.value);
return deletingPolicies?.length ? 1000 : false;
},
refetchOnWindowFocus: false,
});
return {
policies,
loading,
handleFetchPolicies,
policies: data,
isLoading,
handleFetchPolicies: refetch,
};
}
export function useFetchAttachmentPolicyTemplate(options?: {
fetchOnMounted: boolean;
}): useFetchAttachmentPolicyTemplatesReturn {
const { fetchOnMounted } = options || {};
const policyTemplates = ref<PolicyTemplate[]>([] as PolicyTemplate[]);
const loading = ref<boolean>(false);
const handleFetchPolicyTemplates = async () => {
try {
loading.value = true;
export function useFetchAttachmentPolicyTemplate(): useFetchAttachmentPolicyTemplatesReturn {
const { data, isLoading, refetch } = useQuery<PolicyTemplate[]>({
queryKey: ["attachment-policy-templates"],
queryFn: async () => {
const { data } =
await apiClient.extension.storage.policyTemplate.liststorageHaloRunV1alpha1PolicyTemplate();
policyTemplates.value = data.items;
} catch (e) {
console.error("Failed to fetch attachment policy templates", e);
} finally {
loading.value = false;
}
};
onMounted(() => {
fetchOnMounted && handleFetchPolicyTemplates();
return data.items;
},
refetchOnWindowFocus: false,
});
return {
policyTemplates,
loading,
handleFetchPolicyTemplates,
policyTemplates: data,
isLoading,
handleFetchPolicyTemplates: refetch,
};
}

View File

@ -1,32 +1,21 @@
import type {
Attachment,
AttachmentList,
Group,
Policy,
User,
} from "@halo-dev/api-client";
import type { Attachment, Group, Policy, User } from "@halo-dev/api-client";
import type { Ref } from "vue";
import { ref, watch } from "vue";
import type { AttachmentLike } from "@halo-dev/console-shared";
import { apiClient } from "@/utils/api-client";
import { Dialog, Toast } from "@halo-dev/components";
import type { Content, Editor } from "@halo-dev/richtext-editor";
import { onBeforeRouteLeave } from "vue-router";
import { useQuery } from "@tanstack/vue-query";
interface useAttachmentControlReturn {
attachments: Ref<AttachmentList>;
loading: Ref<boolean>;
attachments: Ref<Attachment[] | undefined>;
isLoading: Ref<boolean>;
isFetching: Ref<boolean>;
selectedAttachment: Ref<Attachment | undefined>;
selectedAttachments: Ref<Set<Attachment>>;
checkedAll: Ref<boolean>;
handleFetchAttachments: (options?: { mute?: boolean; page?: number }) => void;
handlePaginationChange: ({
page,
size,
}: {
page: number;
size: number;
}) => void;
total: Ref<number>;
handleFetchAttachments: () => void;
handleSelectPrevious: () => void;
handleSelectNext: () => void;
handleDelete: (attachment: Attachment) => void;
@ -41,124 +30,95 @@ interface useAttachmentSelectReturn {
onAttachmentSelect: (attachments: AttachmentLike[]) => void;
}
export function useAttachmentControl(filterOptions?: {
export function useAttachmentControl(filterOptions: {
policy?: Ref<Policy | undefined>;
group?: Ref<Group | undefined>;
user?: Ref<User | undefined>;
keyword?: Ref<string | undefined>;
sort?: Ref<string | undefined>;
page: Ref<number>;
size: Ref<number>;
}): useAttachmentControlReturn {
const { user, policy, group, keyword, sort } = filterOptions || {};
const attachments = ref<AttachmentList>({
page: 1,
size: 60,
total: 0,
items: [],
first: true,
last: false,
hasNext: false,
hasPrevious: false,
totalPages: 0,
});
const loading = ref<boolean>(false);
const { user, policy, group, keyword, sort, page, size } = filterOptions;
const selectedAttachment = ref<Attachment>();
const selectedAttachments = ref<Set<Attachment>>(new Set<Attachment>());
const checkedAll = ref(false);
const refreshInterval = ref();
const handleFetchAttachments = async (options?: {
mute?: boolean;
page?: number;
}) => {
try {
clearInterval(refreshInterval.value);
if (!options?.mute) {
loading.value = true;
}
if (options?.page) {
attachments.value.page = options.page;
}
const total = ref(0);
const hasPrevious = ref(false);
const hasNext = ref(false);
const { data, isLoading, isFetching, refetch } = useQuery<Attachment[]>({
queryKey: ["attachments", policy, keyword, group, user, page, size, sort],
queryFn: async () => {
const { data } = await apiClient.attachment.searchAttachments({
policy: policy?.value?.metadata.name,
displayName: keyword?.value,
group: group?.value?.metadata.name,
ungrouped: group?.value?.metadata.name === "ungrouped",
uploadedBy: user?.value?.metadata.name,
page: attachments.value.page,
size: attachments.value.size,
page: page?.value,
size: size?.value,
sort: [sort?.value as string].filter(Boolean),
});
attachments.value = data;
const deletedAttachments = attachments.value.items.filter(
total.value = data.total;
hasPrevious.value = data.hasPrevious;
hasNext.value = data.hasNext;
return data.items;
},
refetchInterval(data) {
const deletingAttachments = data?.filter(
(attachment) => !!attachment.metadata.deletionTimestamp
);
if (deletedAttachments.length) {
refreshInterval.value = setInterval(() => {
handleFetchAttachments({ mute: true });
}, 3000);
}
} catch (e) {
console.error("Failed to fetch attachments", e);
} finally {
loading.value = false;
}
};
onBeforeRouteLeave(() => {
clearInterval(refreshInterval.value);
return deletingAttachments?.length ? 3000 : false;
},
refetchOnWindowFocus: false,
});
const handlePaginationChange = async ({
page,
size,
}: {
page: number;
size: number;
}) => {
attachments.value.page = page;
attachments.value.size = size;
await handleFetchAttachments();
};
const handleSelectPrevious = async () => {
const { items, hasPrevious } = attachments.value;
const index = items.findIndex(
if (!data.value) return;
const index = data.value?.findIndex(
(attachment) =>
attachment.metadata.name === selectedAttachment.value?.metadata.name
);
if (index === undefined) return;
if (index > 0) {
selectedAttachment.value = items[index - 1];
selectedAttachment.value = data.value[index - 1];
return;
}
if (index === 0 && hasPrevious) {
attachments.value.page--;
await handleFetchAttachments();
selectedAttachment.value =
attachments.value.items[attachments.value.items.length - 1];
page.value--;
await refetch();
selectedAttachment.value = data.value[data.value.length - 1];
}
};
const handleSelectNext = async () => {
const { items, hasNext } = attachments.value;
const index = items.findIndex(
if (!data.value) return;
const index = data.value?.findIndex(
(attachment) =>
attachment.metadata.name === selectedAttachment.value?.metadata.name
);
if (index < items.length - 1) {
selectedAttachment.value = items[index + 1];
if (index === undefined) return;
if (index < data.value?.length - 1) {
selectedAttachment.value = data.value[index + 1];
return;
}
if (index === items.length - 1 && hasNext) {
attachments.value.page++;
await handleFetchAttachments();
selectedAttachment.value = attachments.value.items[0];
if (index === data.value.length - 1 && hasNext) {
page.value++;
await refetch();
selectedAttachment.value = data.value[0];
}
};
@ -185,7 +145,7 @@ export function useAttachmentControl(filterOptions?: {
} catch (e) {
console.error("Failed to delete attachment", e);
} finally {
await handleFetchAttachments();
await refetch();
}
},
});
@ -214,7 +174,7 @@ export function useAttachmentControl(filterOptions?: {
} catch (e) {
console.error("Failed to delete attachments", e);
} finally {
await handleFetchAttachments();
await refetch();
}
},
});
@ -222,7 +182,7 @@ export function useAttachmentControl(filterOptions?: {
const handleCheckAll = (checkAll: boolean) => {
if (checkAll) {
attachments.value.items.forEach((attachment) => {
data.value?.forEach((attachment) => {
selectedAttachments.value.add(attachment);
});
} else {
@ -242,7 +202,7 @@ export function useAttachmentControl(filterOptions?: {
watch(
() => selectedAttachments.value.size,
(newValue) => {
checkedAll.value = newValue === attachments.value.items?.length;
checkedAll.value = newValue === data.value?.length;
}
);
@ -256,20 +216,21 @@ export function useAttachmentControl(filterOptions?: {
};
const handleReset = () => {
attachments.value.page = 1;
page.value = 1;
selectedAttachment.value = undefined;
selectedAttachments.value.clear();
checkedAll.value = false;
};
return {
attachments,
loading,
attachments: data,
isLoading,
isFetching,
selectedAttachment,
selectedAttachments,
checkedAll,
handleFetchAttachments,
handlePaginationChange,
total,
handleFetchAttachments: refetch,
handleSelectPrevious,
handleSelectNext,
handleDelete,