fix avatar remove button (#12249)
This commit is contained in:
parent
f62c58532d
commit
a519941b81
|
@ -9,8 +9,8 @@ import { ErrorCode } from "@calcom/features/auth/lib/ErrorCode";
|
||||||
import OrganizationMemberAvatar from "@calcom/features/ee/organizations/components/OrganizationMemberAvatar";
|
import OrganizationMemberAvatar from "@calcom/features/ee/organizations/components/OrganizationMemberAvatar";
|
||||||
import SectionBottomActions from "@calcom/features/settings/SectionBottomActions";
|
import SectionBottomActions from "@calcom/features/settings/SectionBottomActions";
|
||||||
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout";
|
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout";
|
||||||
|
import checkIfItFallbackImage from "@calcom/lib/checkIfItFallbackImage";
|
||||||
import { APP_NAME, FULL_NAME_LENGTH_MAX_LIMIT } from "@calcom/lib/constants";
|
import { APP_NAME, FULL_NAME_LENGTH_MAX_LIMIT } from "@calcom/lib/constants";
|
||||||
import { AVATAR_FALLBACK } from "@calcom/lib/constants";
|
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { md } from "@calcom/lib/markdownIt";
|
import { md } from "@calcom/lib/markdownIt";
|
||||||
import turndown from "@calcom/lib/turndownService";
|
import turndown from "@calcom/lib/turndownService";
|
||||||
|
@ -72,32 +72,24 @@ interface DeleteAccountValues {
|
||||||
|
|
||||||
type FormValues = {
|
type FormValues = {
|
||||||
username: string;
|
username: string;
|
||||||
avatar: string | null;
|
avatar: string;
|
||||||
name: string;
|
name: string;
|
||||||
email: string;
|
email: string;
|
||||||
bio: string;
|
bio: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const checkIfItFallbackImage = (fetchedImgSrc?: string) => {
|
|
||||||
return !fetchedImgSrc || fetchedImgSrc.endsWith(AVATAR_FALLBACK);
|
|
||||||
};
|
|
||||||
|
|
||||||
const ProfileView = () => {
|
const ProfileView = () => {
|
||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
const utils = trpc.useContext();
|
const utils = trpc.useContext();
|
||||||
const { update } = useSession();
|
const { update } = useSession();
|
||||||
|
|
||||||
const [fetchedImgSrc, setFetchedImgSrc] = useState<string | undefined>(undefined);
|
const [fetchedImgSrc, setFetchedImgSrc] = useState<string>("");
|
||||||
|
|
||||||
const { data: user, isLoading } = trpc.viewer.me.useQuery(undefined, {
|
const { data: user, isLoading } = trpc.viewer.me.useQuery(undefined, {
|
||||||
onSuccess: async (userData) => {
|
onSuccess: async (userData) => {
|
||||||
try {
|
try {
|
||||||
if (!userData.organization) {
|
|
||||||
const res = await fetch(userData.avatar);
|
const res = await fetch(userData.avatar);
|
||||||
if (res.url) setFetchedImgSrc(res.url);
|
if (res.url) setFetchedImgSrc(res.url);
|
||||||
} else {
|
|
||||||
setFetchedImgSrc("");
|
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setFetchedImgSrc("");
|
setFetchedImgSrc("");
|
||||||
}
|
}
|
||||||
|
@ -234,7 +226,7 @@ const ProfileView = () => {
|
||||||
|
|
||||||
const defaultValues = {
|
const defaultValues = {
|
||||||
username: user.username || "",
|
username: user.username || "",
|
||||||
avatar: user.avatar || "",
|
avatar: fetchedImgSrc || "",
|
||||||
name: user.name || "",
|
name: user.name || "",
|
||||||
email: user.email || "",
|
email: user.email || "",
|
||||||
bio: user.bio || "",
|
bio: user.bio || "",
|
||||||
|
@ -251,8 +243,6 @@ const ProfileView = () => {
|
||||||
key={JSON.stringify(defaultValues)}
|
key={JSON.stringify(defaultValues)}
|
||||||
defaultValues={defaultValues}
|
defaultValues={defaultValues}
|
||||||
isLoading={updateProfileMutation.isLoading}
|
isLoading={updateProfileMutation.isLoading}
|
||||||
isFallbackImg={checkIfItFallbackImage(fetchedImgSrc)}
|
|
||||||
userAvatar={user.avatar}
|
|
||||||
user={user}
|
user={user}
|
||||||
userOrganization={user.organization}
|
userOrganization={user.organization}
|
||||||
onSubmit={(values) => {
|
onSubmit={(values) => {
|
||||||
|
@ -397,8 +387,6 @@ const ProfileForm = ({
|
||||||
onSubmit,
|
onSubmit,
|
||||||
extraField,
|
extraField,
|
||||||
isLoading = false,
|
isLoading = false,
|
||||||
isFallbackImg,
|
|
||||||
userAvatar,
|
|
||||||
user,
|
user,
|
||||||
userOrganization,
|
userOrganization,
|
||||||
}: {
|
}: {
|
||||||
|
@ -406,8 +394,6 @@ const ProfileForm = ({
|
||||||
onSubmit: (values: FormValues) => void;
|
onSubmit: (values: FormValues) => void;
|
||||||
extraField?: React.ReactNode;
|
extraField?: React.ReactNode;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
isFallbackImg: boolean;
|
|
||||||
userAvatar: string;
|
|
||||||
user: RouterOutputs["viewer"]["me"];
|
user: RouterOutputs["viewer"]["me"];
|
||||||
userOrganization: RouterOutputs["viewer"]["me"]["organization"];
|
userOrganization: RouterOutputs["viewer"]["me"]["organization"];
|
||||||
}) => {
|
}) => {
|
||||||
|
@ -416,7 +402,7 @@ const ProfileForm = ({
|
||||||
|
|
||||||
const profileFormSchema = z.object({
|
const profileFormSchema = z.object({
|
||||||
username: z.string(),
|
username: z.string(),
|
||||||
avatar: z.string().nullable(),
|
avatar: z.string(),
|
||||||
name: z
|
name: z
|
||||||
.string()
|
.string()
|
||||||
.trim()
|
.trim()
|
||||||
|
@ -438,7 +424,6 @@ const ProfileForm = ({
|
||||||
} = formMethods;
|
} = formMethods;
|
||||||
|
|
||||||
const isDisabled = isSubmitting || !isDirty;
|
const isDisabled = isSubmitting || !isDirty;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form form={formMethods} handleSubmit={onSubmit}>
|
<Form form={formMethods} handleSubmit={onSubmit}>
|
||||||
<div className="border-subtle border-x px-4 pb-10 pt-8 sm:px-6">
|
<div className="border-subtle border-x px-4 pb-10 pt-8 sm:px-6">
|
||||||
|
@ -447,7 +432,7 @@ const ProfileForm = ({
|
||||||
control={formMethods.control}
|
control={formMethods.control}
|
||||||
name="avatar"
|
name="avatar"
|
||||||
render={({ field: { value } }) => {
|
render={({ field: { value } }) => {
|
||||||
const showRemoveAvatarButton = !isFallbackImg || (value && userAvatar !== value);
|
const showRemoveAvatarButton = !checkIfItFallbackImage(value);
|
||||||
const organization =
|
const organization =
|
||||||
userOrganization && userOrganization.id
|
userOrganization && userOrganization.id
|
||||||
? {
|
? {
|
||||||
|
@ -474,7 +459,7 @@ const ProfileForm = ({
|
||||||
handleAvatarChange={(newAvatar) => {
|
handleAvatarChange={(newAvatar) => {
|
||||||
formMethods.setValue("avatar", newAvatar, { shouldDirty: true });
|
formMethods.setValue("avatar", newAvatar, { shouldDirty: true });
|
||||||
}}
|
}}
|
||||||
imageSrc={value || undefined}
|
imageSrc={value}
|
||||||
triggerButtonColor={showRemoveAvatarButton ? "secondary" : "primary"}
|
triggerButtonColor={showRemoveAvatarButton ? "secondary" : "primary"}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
@ -482,7 +467,7 @@ const ProfileForm = ({
|
||||||
<Button
|
<Button
|
||||||
color="secondary"
|
color="secondary"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
formMethods.setValue("avatar", null, { shouldDirty: true });
|
formMethods.setValue("avatar", "", { shouldDirty: true });
|
||||||
}}>
|
}}>
|
||||||
{t("remove")}
|
{t("remove")}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
import { AVATAR_FALLBACK } from "./constants";
|
||||||
|
|
||||||
|
const checkIfItFallbackImage = (fetchedImgSrc: string) => {
|
||||||
|
return !fetchedImgSrc || fetchedImgSrc.endsWith(AVATAR_FALLBACK);
|
||||||
|
};
|
||||||
|
export default checkIfItFallbackImage;
|
|
@ -36,7 +36,7 @@ export const updateProfileHandler = async ({ ctx, input }: UpdateProfileOptions)
|
||||||
const userMetadata = handleUserMetadata({ ctx, input });
|
const userMetadata = handleUserMetadata({ ctx, input });
|
||||||
const data: Prisma.UserUpdateInput = {
|
const data: Prisma.UserUpdateInput = {
|
||||||
...input,
|
...input,
|
||||||
avatar: await getAvatarToSet(input.avatar),
|
avatar: input.avatar ? await getAvatarToSet(input.avatar) : null,
|
||||||
metadata: userMetadata,
|
metadata: userMetadata,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ import type { FormEvent } from "react";
|
||||||
import { useCallback, useEffect, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
import Cropper from "react-easy-crop";
|
import Cropper from "react-easy-crop";
|
||||||
|
|
||||||
|
import checkIfItFallbackImage from "@calcom/lib/checkIfItFallbackImage";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
|
|
||||||
import type { ButtonColor } from "../..";
|
import type { ButtonColor } from "../..";
|
||||||
|
@ -120,20 +121,15 @@ export default function ImageUploader({
|
||||||
buttonMsg,
|
buttonMsg,
|
||||||
handleAvatarChange,
|
handleAvatarChange,
|
||||||
triggerButtonColor,
|
triggerButtonColor,
|
||||||
...props
|
imageSrc,
|
||||||
}: ImageUploaderProps) {
|
}: ImageUploaderProps) {
|
||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
const [imageSrc, setImageSrc] = useState<string | null>(null);
|
|
||||||
const [croppedAreaPixels, setCroppedAreaPixels] = useState<Area | null>(null);
|
const [croppedAreaPixels, setCroppedAreaPixels] = useState<Area | null>(null);
|
||||||
|
|
||||||
const [{ result }, setFile] = useFileReader({
|
const [{ result }, setFile] = useFileReader({
|
||||||
method: "readAsDataURL",
|
method: "readAsDataURL",
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (props.imageSrc) setImageSrc(props.imageSrc);
|
|
||||||
}, [props.imageSrc]);
|
|
||||||
|
|
||||||
const onInputFile = (e: FileEvent<HTMLInputElement>) => {
|
const onInputFile = (e: FileEvent<HTMLInputElement>) => {
|
||||||
if (!e.target.files?.length) {
|
if (!e.target.files?.length) {
|
||||||
return;
|
return;
|
||||||
|
@ -157,7 +153,6 @@ export default function ImageUploader({
|
||||||
result as string /* result is always string when using readAsDataUrl */,
|
result as string /* result is always string when using readAsDataUrl */,
|
||||||
croppedAreaPixels
|
croppedAreaPixels
|
||||||
);
|
);
|
||||||
setImageSrc(croppedImage);
|
|
||||||
handleAvatarChange(croppedImage);
|
handleAvatarChange(croppedImage);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
|
@ -181,12 +176,11 @@ export default function ImageUploader({
|
||||||
<div className="cropper mt-6 flex flex-col items-center justify-center p-8">
|
<div className="cropper mt-6 flex flex-col items-center justify-center p-8">
|
||||||
{!result && (
|
{!result && (
|
||||||
<div className="bg-muted flex h-20 max-h-20 w-20 items-center justify-start rounded-full">
|
<div className="bg-muted flex h-20 max-h-20 w-20 items-center justify-start rounded-full">
|
||||||
{!imageSrc && (
|
{!imageSrc || checkIfItFallbackImage(imageSrc) ? (
|
||||||
<p className="text-emphasis w-full text-center text-sm sm:text-xs">
|
<p className="text-emphasis w-full text-center text-sm sm:text-xs">
|
||||||
{t("no_target", { target })}
|
{t("no_target", { target })}
|
||||||
</p>
|
</p>
|
||||||
)}
|
) : (
|
||||||
{imageSrc && (
|
|
||||||
// eslint-disable-next-line @next/next/no-img-element
|
// eslint-disable-next-line @next/next/no-img-element
|
||||||
<img className="h-20 w-20 rounded-full" src={imageSrc} alt={target} />
|
<img className="h-20 w-20 rounded-full" src={imageSrc} alt={target} />
|
||||||
)}
|
)}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user