refactor: use tanstack query to refactor theme-related fetching (#3588)

#### 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/3576/head^2
Ryan Wang 2023-03-27 15:56:17 +08:00 committed by GitHub
parent bd9f590c1e
commit 5b3b473cb3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 69 additions and 93 deletions

View File

@ -17,11 +17,11 @@ import LazyImage from "@/components/image/LazyImage.vue";
import ThemePreviewModal from "./preview/ThemePreviewModal.vue";
import ThemeUploadModal from "./ThemeUploadModal.vue";
import ThemeListItem from "./components/ThemeListItem.vue";
import { computed, ref, watch } from "vue";
import { computed, ref } from "vue";
import type { Theme } from "@halo-dev/api-client";
import { apiClient } from "@/utils/api-client";
import { onBeforeRouteLeave } from "vue-router";
import { useI18n } from "vue-i18n";
import { useQuery } from "@tanstack/vue-query";
const { t } = useI18n();
@ -44,11 +44,8 @@ const emit = defineEmits<{
}>();
const activeTab = ref("installed");
const themes = ref<Theme[]>([] as Theme[]);
const loading = ref(false);
const themeUploadVisible = ref(false);
const creating = ref(false);
const refreshInterval = ref();
const modalTitle = computed(() => {
return activeTab.value === "installed"
@ -56,51 +53,36 @@ const modalTitle = computed(() => {
: t("core.theme.list_modal.titles.not_installed_themes");
});
const handleFetchThemes = async (options?: { mute?: boolean }) => {
try {
clearInterval(refreshInterval.value);
if (!options?.mute) {
loading.value = true;
}
const {
data: themes,
isLoading,
isFetching,
refetch,
} = useQuery<Theme[]>({
queryKey: ["themes", activeTab],
queryFn: async () => {
const { data } = await apiClient.theme.listThemes({
page: 0,
size: 0,
uninstalled: activeTab.value !== "installed",
});
themes.value = data.items;
return data.items;
},
refetchOnWindowFocus: false,
refetchInterval(data) {
if (activeTab.value !== "installed") {
return;
return false;
}
const deletedThemes = themes.value.filter(
const deletingThemes = data?.filter(
(theme) => !!theme.metadata.deletionTimestamp
);
if (deletedThemes.length) {
refreshInterval.value = setInterval(() => {
handleFetchThemes({ mute: true });
}, 3000);
}
} catch (e) {
console.error("Failed to fetch themes", e);
} finally {
loading.value = false;
}
};
onBeforeRouteLeave(() => {
clearInterval(refreshInterval.value);
return deletingThemes?.length ? 3000 : false;
},
enabled: computed(() => props.visible),
});
watch(
() => activeTab.value,
() => {
handleFetchThemes();
}
);
const handleCreateTheme = async (theme: Theme) => {
try {
creating.value = true;
@ -120,7 +102,7 @@ const handleCreateTheme = async (theme: Theme) => {
console.error("Failed to create theme", error);
} finally {
creating.value = false;
handleFetchThemes();
refetch();
}
};
@ -137,19 +119,8 @@ const handleSelectTheme = (theme: Theme) => {
onVisibleChange(false);
};
watch(
() => props.visible,
(visible) => {
if (visible) {
handleFetchThemes();
} else {
clearInterval(refreshInterval.value);
}
}
);
defineExpose({
handleFetchThemes,
handleFetchThemes: refetch,
});
// preview
@ -193,15 +164,15 @@ const handleOpenInstallModal = () => {
:label="$t('core.theme.list_modal.tabs.installed')"
class="-mx-[16px]"
>
<VLoading v-if="loading" />
<Transition v-else-if="!themes.length" appear name="fade">
<VLoading v-if="isLoading" />
<Transition v-else-if="!themes?.length" appear name="fade">
<VEmpty
:message="$t('core.theme.list_modal.empty.message')"
:title="$t('core.theme.list_modal.empty.title')"
>
<template #actions>
<VSpace>
<VButton :loading="loading" @click="handleFetchThemes()">
<VButton :loading="isFetching" @click="refetch()">
{{ $t("core.common.buttons.refresh") }}
</VButton>
<VButton
@ -233,7 +204,7 @@ const handleOpenInstallModal = () => {
:is-selected="
theme.metadata.name === selectedTheme?.metadata?.name
"
@reload="handleFetchThemes({ mute: true })"
@reload="refetch"
@preview="handleOpenPreview(theme)"
@upgrade="handleOpenUpgradeModal(theme)"
/>
@ -246,14 +217,14 @@ const handleOpenInstallModal = () => {
:label="$t('core.theme.list_modal.tabs.not_installed')"
class="-mx-[16px]"
>
<VLoading v-if="loading" />
<Transition v-else-if="!themes.length" appear name="fade">
<VLoading v-if="isLoading" />
<Transition v-else-if="!themes?.length" appear name="fade">
<VEmpty
:title="$t('core.theme.list_modal.not_installed_empty.title')"
>
<template #actions>
<VSpace>
<VButton :loading="loading" @click="handleFetchThemes">
<VButton :loading="isFetching" @click="refetch">
{{ $t("core.common.buttons.refresh") }}
</VButton>
</VSpace>
@ -372,7 +343,7 @@ const handleOpenInstallModal = () => {
v-if="visible"
v-model:visible="themeUploadVisible"
:upgrade-theme="themeToUpgrade"
@close="handleFetchThemes"
@close="refetch"
/>
<ThemePreviewModal

View File

@ -26,6 +26,7 @@ import {
import { storeToRefs } from "pinia";
import { computed, markRaw, ref, watch } from "vue";
import { useI18n } from "vue-i18n";
import { useQuery } from "@tanstack/vue-query";
const props = withDefaults(
defineProps<{
@ -55,33 +56,34 @@ interface SettingTab {
const { activatedTheme } = storeToRefs(useThemeStore());
const previewFrame = ref<HTMLIFrameElement | null>(null);
const themes = ref<Theme[]>([] as Theme[]);
const themesVisible = ref(false);
const switching = ref(false);
const selectedTheme = ref<Theme>();
const handleFetchThemes = async () => {
try {
const { data: themes } = useQuery<Theme[]>({
queryKey: ["themes"],
queryFn: async () => {
const { data } = await apiClient.theme.listThemes({
page: 0,
size: 0,
uninstalled: false,
});
themes.value = data.items;
} catch (e) {
console.error("Failed to fetch themes", e);
}
};
return data.items;
},
refetchOnWindowFocus: false,
enabled: computed(() => props.visible),
});
watch(
() => props.visible,
(visible) => {
if (visible) {
handleFetchThemes();
selectedTheme.value = props.theme || activatedTheme?.value;
} else {
themesVisible.value = false;
settingsVisible.value = false;
setTimeout(() => {
themesVisible.value = false;
settingsVisible.value = false;
}, 200);
}
}
);
@ -121,39 +123,42 @@ const modalTitle = computed(() => {
});
// theme settings
const setting = ref<Setting>();
const configMap = ref<ConfigMap>();
const saving = ref(false);
const settingTabs = ref<SettingTab[]>([] as SettingTab[]);
const activeSettingTab = ref("");
const settingsVisible = ref(false);
const { data: setting, refetch: handleFetchSettings } = useQuery<Setting>({
queryKey: ["theme-setting", selectedTheme],
queryFn: async () => {
const { data } = await apiClient.theme.fetchThemeSetting({
name: selectedTheme?.value?.metadata.name as string,
});
return data;
},
refetchOnWindowFocus: false,
enabled: computed(() => !!selectedTheme.value?.spec.settingName),
});
const { data: configMap, refetch: handleFetchConfigMap } = useQuery<ConfigMap>({
queryKey: ["theme-configMap", selectedTheme],
queryFn: async () => {
const { data } = await apiClient.theme.fetchThemeConfig({
name: selectedTheme?.value?.metadata.name as string,
});
return data;
},
refetchOnWindowFocus: false,
enabled: computed(() => !!selectedTheme.value?.spec.configMapName),
});
const { formSchema, configMapFormData, convertToSave } = useSettingFormConvert(
setting,
configMap,
activeSettingTab
);
const handleFetchSettings = async () => {
if (!selectedTheme?.value) return;
const { data } = await apiClient.theme.fetchThemeSetting({
name: selectedTheme?.value?.metadata.name,
});
setting.value = data;
};
const handleFetchConfigMap = async () => {
if (!selectedTheme?.value) return;
const { data } = await apiClient.theme.fetchThemeConfig({
name: selectedTheme?.value?.metadata.name,
});
configMap.value = data;
};
const handleSaveConfigMap = async () => {
saving.value = true;
@ -164,7 +169,7 @@ const handleSaveConfigMap = async () => {
return;
}
const { data: newConfigMap } = await apiClient.theme.updateThemeConfig({
await apiClient.theme.updateThemeConfig({
name: selectedTheme?.value?.metadata.name,
configMap: configMapToUpdate,
});
@ -172,7 +177,7 @@ const handleSaveConfigMap = async () => {
Toast.success(t("core.common.toast.save_success"));
await handleFetchSettings();
configMap.value = newConfigMap;
await handleFetchConfigMap();
saving.value = false;