fix avatar remove button (#12249)

This commit is contained in:
Mehul 2023-11-15 15:23:33 +05:30 committed by GitHub
parent f62c58532d
commit a519941b81
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 21 additions and 36 deletions

View File

@ -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>

View File

@ -0,0 +1,6 @@
import { AVATAR_FALLBACK } from "./constants";
const checkIfItFallbackImage = (fetchedImgSrc: string) => {
return !fetchedImgSrc || fetchedImgSrc.endsWith(AVATAR_FALLBACK);
};
export default checkIfItFallbackImage;

View File

@ -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,
}; };

View File

@ -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} />
)} )}