From f9e252f0c9749d42f48531983a4c6ff0117abf2d Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal Date: Tue, 19 Nov 2024 17:32:16 +0530 Subject: [PATCH 1/3] fix: cover image update payload --- packages/types/src/project/projects.d.ts | 5 ++++- packages/types/src/users.d.ts | 5 ++++- web/app/profile/page.tsx | 2 +- web/ce/components/projects/create/root.tsx | 1 + web/core/components/project/form.tsx | 2 +- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/types/src/project/projects.d.ts b/packages/types/src/project/projects.d.ts index 75d6668b8a5..b8e69da9f9b 100644 --- a/packages/types/src/project/projects.d.ts +++ b/packages/types/src/project/projects.d.ts @@ -18,7 +18,10 @@ export interface IProject { close_in: number; created_at: Date; created_by: string; - cover_image_url: string; + // only for uploading the cover image + 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..7f1178c64a2 100644 --- a/packages/types/src/users.d.ts +++ b/packages/types/src/users.d.ts @@ -14,7 +14,10 @@ export interface IUserLite { last_name: string; } export interface IUser extends IUserLite { - cover_image_url: string | null; + // only for uploading the cover image + cover_image: string | null; + // only for rendering the cover image + cover_image_url: readonly (string | null); date_joined: string; email: string; is_active: boolean; diff --git a/web/app/profile/page.tsx b/web/app/profile/page.tsx index 1ec755bd1e1..a78d7db654f 100644 --- a/web/app/profile/page.tsx +++ b/web/app/profile/page.tsx @@ -70,7 +70,7 @@ 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, + cover_image: formData.cover_image_url, role: formData.role, display_name: formData?.display_name, user_timezone: formData.user_timezone, diff --git a/web/ce/components/projects/create/root.tsx b/web/ce/components/projects/create/root.tsx index 04c6dfc882f..27f97783b7b 100644 --- a/web/ce/components/projects/create/root.tsx +++ b/web/ce/components/projects/create/root.tsx @@ -73,6 +73,7 @@ export const CreateProjectForm: FC = observer((props) = const onSubmit = async (formData: Partial) => { // Upper case identifier formData.identifier = formData.identifier?.toUpperCase(); + formData.cover_image = formData.cover_image_url; const coverImage = formData.cover_image_url; return createProject(workspaceSlug.toString(), formData) diff --git a/web/core/components/project/form.tsx b/web/core/components/project/form.tsx index 3a076ce29ef..efce4e64bdd 100644 --- a/web/core/components/project/form.tsx +++ b/web/core/components/project/form.tsx @@ -144,7 +144,7 @@ export const ProjectDetailsForm: FC = (props) => { network: formData.network, identifier: formData.identifier, description: formData.description, - cover_image_url: formData.cover_image_url, + cover_image: formData.cover_image_url, logo_props: formData.logo_props, // timezone: formData.timezone, }; From 77790b3a075d4c7ade9ef665c13dab2c309db352 Mon Sep 17 00:00:00 2001 From: pablohashescobar Date: Tue, 19 Nov 2024 18:13:43 +0530 Subject: [PATCH 2/3] fix: cover image assets --- apiserver/plane/app/views/asset/v2.py | 24 +++++++++++++++++++--- packages/types/src/project/projects.d.ts | 1 + packages/types/src/users.d.ts | 14 +++++++++---- web/app/profile/page.tsx | 6 +++++- web/ce/components/projects/create/root.tsx | 6 +++++- web/core/components/project/form.tsx | 7 ++++++- 6 files changed, 48 insertions(+), 10 deletions(-) diff --git a/apiserver/plane/app/views/asset/v2.py b/apiserver/plane/app/views/asset/v2.py index aa777fff700..ca3ec9c74de 100644 --- a/apiserver/plane/app/views/asset/v2.py +++ b/apiserver/plane/app/views/asset/v2.py @@ -146,7 +146,13 @@ 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", + "image/gif", + ] if type not in allowed_types: return Response( { @@ -317,7 +323,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 @@ -620,7 +626,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 +750,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 +790,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 b8e69da9f9b..d48342cebb9 100644 --- a/packages/types/src/project/projects.d.ts +++ b/packages/types/src/project/projects.d.ts @@ -19,6 +19,7 @@ export interface IProject { created_at: Date; created_by: 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; diff --git a/packages/types/src/users.d.ts b/packages/types/src/users.d.ts index 7f1178c64a2..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; @@ -15,7 +14,8 @@ export interface IUserLite { } export interface IUser extends IUserLite { // only for uploading the cover image - cover_image: string | null; + cover_image_asset?: string | null; + cover_image?: string | null; // only for rendering the cover image cover_image_url: readonly (string | null); date_joined: string; @@ -93,7 +93,6 @@ export interface IUserTheme { sidebarBackground: string | undefined; } - export interface IUserMemberLite extends IUserLite { email?: string; } @@ -156,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 a78d7db654f..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: 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 27f97783b7b..c7dabd20e55 100644 --- a/web/ce/components/projects/create/root.tsx +++ b/web/ce/components/projects/create/root.tsx @@ -73,8 +73,12 @@ export const CreateProjectForm: FC = observer((props) = const onSubmit = async (formData: Partial) => { // Upper case identifier formData.identifier = formData.identifier?.toUpperCase(); - formData.cover_image = formData.cover_image_url; 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 efce4e64bdd..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: 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 From 259e35b2719437fc675359432a78e28df8f75f9e Mon Sep 17 00:00:00 2001 From: pablohashescobar Date: Tue, 19 Nov 2024 18:23:47 +0530 Subject: [PATCH 3/3] chore: add gif support --- apiserver/plane/app/views/asset/v2.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/apiserver/plane/app/views/asset/v2.py b/apiserver/plane/app/views/asset/v2.py index ca3ec9c74de..16e82de59ad 100644 --- a/apiserver/plane/app/views/asset/v2.py +++ b/apiserver/plane/app/views/asset/v2.py @@ -151,7 +151,6 @@ def post(self, request): "image/png", "image/webp", "image/jpg", - "image/gif", ] if type not in allowed_types: return Response( @@ -393,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( {