From 192b238e376aca55d704f86420d001090ba3b7fb Mon Sep 17 00:00:00 2001 From: Ryan Wang Date: Mon, 29 Jan 2024 16:06:02 +0800 Subject: [PATCH] fix: it cannot be re-uploaded when the user's avatar cannot be obtained (#5270) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #### What type of PR is this? /area console /kind bug /milestone 2.12.x #### What this PR does / why we need it: 修复当头像因为后端服务无法正常变更时,前端因为一直处于 refetch 状态导致无法重新上传的问题。 #### Which issue(s) this PR fixes: Fixes https://github.com/halo-dev/halo/issues/4852 #### Special notes for your reviewer: 1. 使用 https://github.com/AirboZH/plugin-uposs 存储服务创建一个存储策略。 2. 用户设置中的头像存储位置改为此存储策略。 3. 上传头像,观测是否一直处于重新获取的状态。 #### Does this PR introduce a user-facing change? ```release-note 优化头像上传,防止因为后端服务异常导致无法重新上传。 ``` --- .../modules/system/users/UserDetail.vue | 13 +---- .../src/components/user-avatar/UserAvatar.vue | 58 +++++++++++++------ console/uc-src/modules/profile/Profile.vue | 13 +---- 3 files changed, 41 insertions(+), 43 deletions(-) diff --git a/console/console-src/modules/system/users/UserDetail.vue b/console/console-src/modules/system/users/UserDetail.vue index f7f607b5a..2355ac07f 100644 --- a/console/console-src/modules/system/users/UserDetail.vue +++ b/console/console-src/modules/system/users/UserDetail.vue @@ -5,7 +5,6 @@ import { VTabbar, VDropdown, VDropdownItem, - VLoading, } from "@halo-dev/components"; import { computed, provide, ref, type Ref } from "vue"; import { useRoute } from "vue-router"; @@ -15,7 +14,6 @@ import UserPasswordChangeModal from "./components/UserPasswordChangeModal.vue"; import { usePermission } from "@/utils/permission"; import { useQuery } from "@tanstack/vue-query"; import { useI18n } from "vue-i18n"; -import { rbacAnnotations } from "@/constants/annotations"; import UserAvatar from "@/components/user-avatar/UserAvatar.vue"; import type { Raw } from "vue"; import type { Component } from "vue"; @@ -45,7 +43,6 @@ const { params } = useRoute(); const { data: user, - isFetching, isLoading, refetch, } = useQuery({ @@ -56,13 +53,6 @@ const { }); return data; }, - refetchInterval: (data) => { - const annotations = data?.user.metadata.annotations; - return annotations?.[rbacAnnotations.AVATAR_ATTACHMENT_NAME] !== - annotations?.[rbacAnnotations.LAST_AVATAR_ATTACHMENT_NAME] - ? 1000 - : false; - }, enabled: computed(() => !!params.name), }); @@ -105,8 +95,7 @@ function handleRouteToUC() {
- - +

diff --git a/console/src/components/user-avatar/UserAvatar.vue b/console/src/components/user-avatar/UserAvatar.vue index f15f41589..4d5b02447 100644 --- a/console/src/components/user-avatar/UserAvatar.vue +++ b/console/src/components/user-avatar/UserAvatar.vue @@ -11,21 +11,23 @@ import { VSpace, Toast, Dialog, + VLoading, } from "@halo-dev/components"; import { ref, defineAsyncComponent, type Ref } from "vue"; -import type { DetailedUser } from "@halo-dev/api-client"; import { usePermission } from "@/utils/permission"; -import { useQueryClient } from "@tanstack/vue-query"; +import { useQuery, useQueryClient } from "@tanstack/vue-query"; import { useI18n } from "vue-i18n"; import { useFileDialog } from "@vueuse/core"; -import { inject } from "vue"; import { computed } from "vue"; +import { rbacAnnotations } from "@/constants/annotations"; const props = withDefaults( defineProps<{ + name?: string; isCurrentUser?: boolean; }>(), { + name: "-", isCurrentUser: false, } ); @@ -34,6 +36,31 @@ const queryClient = useQueryClient(); const { currentUserHasPermission } = usePermission(); const { t } = useI18n(); +const { data: avatar, isFetching } = useQuery({ + queryKey: ["user-avatar", props.name, props.isCurrentUser], + queryFn: async () => { + const { data } = props.isCurrentUser + ? await apiClient.user.getCurrentUserDetail() + : await apiClient.user.getUserDetail({ + name: props.name, + }); + + const annotations = data?.user.metadata.annotations; + + // Check avatar has been updated. if not, we need retry. + if ( + annotations?.[rbacAnnotations.AVATAR_ATTACHMENT_NAME] !== + annotations?.[rbacAnnotations.LAST_AVATAR_ATTACHMENT_NAME] + ) { + throw new Error("Avatar is not updated"); + } + + return data.user.spec.avatar || ""; + }, + retry: 5, + retryDelay: 1000, +}); + const UserAvatarCropper = defineAsyncComponent( () => import("./UserAvatarCropper.vue") ); @@ -48,8 +75,6 @@ const { open, reset, onChange } = useFileDialog({ multiple: false, }); -const user = inject>("user"); - const userAvatarCropper = ref(); const visibleCropperModal = ref(false); const originalFile = ref() as Ref; @@ -67,18 +92,15 @@ onChange((files) => { const uploadSaving = ref(false); const handleUploadAvatar = () => { userAvatarCropper.value?.getCropperFile().then((file) => { - if (!user?.value) { - return; - } - uploadSaving.value = true; apiClient.user .uploadUserAvatar({ - name: props.isCurrentUser ? "-" : user.value.user.metadata.name, + name: props.isCurrentUser ? "-" : props.name, file: file, }) .then(() => { + queryClient.invalidateQueries({ queryKey: ["user-avatar"] }); queryClient.invalidateQueries({ queryKey: ["user-detail"] }); handleCloseCropperModal(); }) @@ -99,15 +121,12 @@ const handleRemoveCurrentAvatar = () => { confirmText: t("core.common.buttons.confirm"), cancelText: t("core.common.buttons.cancel"), onConfirm: async () => { - if (!user?.value) { - return; - } - apiClient.user .deleteUserAvatar({ - name: props.isCurrentUser ? "-" : user.value.user.metadata.name, + name: props.isCurrentUser ? "-" : props.name, }) .then(() => { + queryClient.invalidateQueries({ queryKey: ["user-avatar"] }); queryClient.invalidateQueries({ queryKey: ["user-detail"] }); }) .catch(() => { @@ -128,15 +147,16 @@ const changeUploadAvatar = () => { }; const hasAvatar = computed(() => { - return !!user?.value?.user.spec.avatar; + return !!avatar.value; });