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;
});
- ]
+
+
{
- const annotations = data?.user.metadata.annotations;
- return annotations?.[rbacAnnotations.AVATAR_ATTACHMENT_NAME] !==
- annotations?.[rbacAnnotations.LAST_AVATAR_ATTACHMENT_NAME]
- ? 1000
- : false;
- },
});
provide[>("user", user);
@@ -110,8 +100,7 @@ const activeTab = useRouteQuery("tab", tabs[0].id, {
]