From d5a55de17a808289485b4eb2ce202d1393088dbd Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Tue, 19 Nov 2024 18:28:53 +0530 Subject: [PATCH] fix: cover image update fix for project and user profile (#6075) * fix: cover image update payload * fix: cover image assets * chore: add gif support --------- Co-authored-by: pablohashescobar --- apiserver/plane/app/views/asset/v2.py | 31 +++++++++++++++++++--- packages/types/src/project/projects.d.ts | 6 ++++- packages/types/src/users.d.ts | 17 +++++++++--- web/app/profile/page.tsx | 6 ++++- web/ce/components/projects/create/root.tsx | 5 ++++ web/core/components/project/form.tsx | 7 ++++- 6 files changed, 61 insertions(+), 11 deletions(-) diff --git a/apiserver/plane/app/views/asset/v2.py b/apiserver/plane/app/views/asset/v2.py index aa777fff700..16e82de59ad 100644 --- a/apiserver/plane/app/views/asset/v2.py +++ b/apiserver/plane/app/views/asset/v2.py @@ -146,7 +146,12 @@ def post(self, request): ) # Check if the file type is allowed - allowed_types = ["image/jpeg", "image/png", "image/webp", "image/jpg"] + allowed_types = [ + "image/jpeg", + "image/png", + "image/webp", + "image/jpg", + ] if type not in allowed_types: return Response( { @@ -317,7 +322,7 @@ def entity_asset_save(self, asset_id, entity_type, asset, request): # Project Cover elif entity_type == FileAsset.EntityTypeContext.PROJECT_COVER: - project = Project.objects.filter(id=asset.workspace_id).first() + project = Project.objects.filter(id=asset.project_id).first() if project is None: return # Delete the previous cover image @@ -387,7 +392,13 @@ def post(self, request, slug): ) # Check if the file type is allowed - allowed_types = ["image/jpeg", "image/png", "image/webp", "image/jpg"] + allowed_types = [ + "image/jpeg", + "image/png", + "image/webp", + "image/jpg", + "image/gif", + ] if type not in allowed_types: return Response( { @@ -620,7 +631,13 @@ def post(self, request, slug, project_id): ) # Check if the file type is allowed - allowed_types = ["image/jpeg", "image/png", "image/webp", "image/jpg"] + allowed_types = [ + "image/jpeg", + "image/png", + "image/webp", + "image/jpg", + "image/gif", + ] if type not in allowed_types: return Response( { @@ -738,6 +755,11 @@ def get(self, request, slug, project_id, pk): class ProjectBulkAssetEndpoint(BaseAPIView): + def save_project_cover(self, asset, project_id): + project = Project.objects.get(id=project_id) + project.cover_image_asset_id = asset.id + project.save() + @allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST]) def post(self, request, slug, project_id, entity_id): asset_ids = request.data.get("asset_ids", []) @@ -773,6 +795,7 @@ def post(self, request, slug, project_id, entity_id): assets.update( project_id=project_id, ) + [self.save_project_cover(asset, project_id) for asset in assets] if asset.entity_type == FileAsset.EntityTypeContext.ISSUE_DESCRIPTION: assets.update( diff --git a/packages/types/src/project/projects.d.ts b/packages/types/src/project/projects.d.ts index 75d6668b8a5..d48342cebb9 100644 --- a/packages/types/src/project/projects.d.ts +++ b/packages/types/src/project/projects.d.ts @@ -18,7 +18,11 @@ export interface IProject { close_in: number; created_at: Date; created_by: string; - cover_image_url: string; + // only for uploading the cover image + cover_image_asset?: null; + cover_image?: string; + // only for rendering the cover image + cover_image_url: readonly string; cycle_view: boolean; issue_views_view: boolean; module_view: boolean; diff --git a/packages/types/src/users.d.ts b/packages/types/src/users.d.ts index 0440ff05f93..452bc23c238 100644 --- a/packages/types/src/users.d.ts +++ b/packages/types/src/users.d.ts @@ -3,7 +3,6 @@ import { TUserPermissions } from "./enums"; type TLoginMediums = "email" | "magic-code" | "github" | "gitlab" | "google"; - export interface IUserLite { avatar_url: string; display_name: string; @@ -14,7 +13,11 @@ export interface IUserLite { last_name: string; } export interface IUser extends IUserLite { - cover_image_url: string | null; + // only for uploading the cover image + cover_image_asset?: string | null; + cover_image?: string | null; + // only for rendering the cover image + cover_image_url: readonly (string | null); date_joined: string; email: string; is_active: boolean; @@ -90,7 +93,6 @@ export interface IUserTheme { sidebarBackground: string | undefined; } - export interface IUserMemberLite extends IUserLite { email?: string; } @@ -153,7 +155,14 @@ export interface IUserProfileProjectSegregation { id: string; pending_issues: number; }[]; - user_data: Pick & { + user_data: Pick< + IUser, + | "avatar_url" + | "cover_image_url" + | "display_name" + | "first_name" + | "last_name" + > & { date_joined: Date; user_timezone: string; }; diff --git a/web/app/profile/page.tsx b/web/app/profile/page.tsx index 1ec755bd1e1..1dd9702a36b 100644 --- a/web/app/profile/page.tsx +++ b/web/app/profile/page.tsx @@ -70,11 +70,15 @@ const ProfileSettingsPage = observer(() => { first_name: formData.first_name, last_name: formData.last_name, avatar_url: formData.avatar_url, - cover_image_url: formData.cover_image_url, role: formData.role, display_name: formData?.display_name, user_timezone: formData.user_timezone, }; + // if unsplash or a pre-defined image is uploaded, delete the old uploaded asset + if (formData.cover_image_url?.startsWith("http")) { + payload.cover_image = formData.cover_image_url; + payload.cover_image_asset = null; + } const updateCurrentUserDetail = updateCurrentUser(payload).finally(() => setIsLoading(false)); setPromiseToast(updateCurrentUserDetail, { diff --git a/web/ce/components/projects/create/root.tsx b/web/ce/components/projects/create/root.tsx index 04c6dfc882f..c7dabd20e55 100644 --- a/web/ce/components/projects/create/root.tsx +++ b/web/ce/components/projects/create/root.tsx @@ -74,6 +74,11 @@ export const CreateProjectForm: FC = observer((props) = // Upper case identifier formData.identifier = formData.identifier?.toUpperCase(); const coverImage = formData.cover_image_url; + // if unsplash or a pre-defined image is uploaded, delete the old uploaded asset + if (coverImage?.startsWith("http")) { + formData.cover_image = coverImage; + formData.cover_image_asset = null; + } return createProject(workspaceSlug.toString(), formData) .then(async (res) => { diff --git a/web/core/components/project/form.tsx b/web/core/components/project/form.tsx index 3a076ce29ef..86da4a2f797 100644 --- a/web/core/components/project/form.tsx +++ b/web/core/components/project/form.tsx @@ -144,10 +144,15 @@ export const ProjectDetailsForm: FC = (props) => { network: formData.network, identifier: formData.identifier, description: formData.description, - cover_image_url: formData.cover_image_url, + logo_props: formData.logo_props, // timezone: formData.timezone, }; + // if unsplash or a pre-defined image is uploaded, delete the old uploaded asset + if (formData.cover_image_url?.startsWith("http")) { + payload.cover_image = formData.cover_image_url; + payload.cover_image_asset = null; + } if (project.identifier !== formData.identifier) await projectService