Fix application icons (#20142)

fixes application chip (icon Name) in all setting tables

## After
<img width="1200" height="896" alt="image"
src="https://github.com/user-attachments/assets/bd377f47-1d52-4142-b904-f2ce90c1db78"
/>
<img width="1200" height="917" alt="image"
src="https://github.com/user-attachments/assets/f49cc742-f11e-47e3-86ed-34beffe493c7"
/>
<img width="1234" height="878" alt="image"
src="https://github.com/user-attachments/assets/2ab459de-5f9d-4d39-9490-eec4ed9ee432"
/>
<img width="1239" height="845" alt="image"
src="https://github.com/user-attachments/assets/3c1bf258-285a-47b9-a60d-05ba1564334d"
/>
<img width="1183" height="907" alt="image"
src="https://github.com/user-attachments/assets/715b2470-2d88-48e3-88ac-d3daf3451717"
/>
<img width="1300" height="912" alt="image"
src="https://github.com/user-attachments/assets/d7c829fa-bf1d-4f19-82de-a8bf29e22bfa"
/>
This commit is contained in:
martmull
2026-04-30 11:32:04 +02:00
committed by GitHub
parent 842e679cc6
commit bddd23fd9c
59 changed files with 772 additions and 205 deletions
@@ -5,6 +5,8 @@ import { promises as fs } from 'fs';
import { resolve } from 'path';
import semver from 'semver';
import { extractFileInfo } from 'src/engine/core-modules/file/utils/extract-file-info.utils';
import { sanitizeFile } from 'src/engine/core-modules/file/utils/sanitize-file.utils';
import { Manifest } from 'twenty-shared/application';
import { FileFolder } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
@@ -141,6 +143,7 @@ export class ApplicationInstallService {
existingApplication,
universalIdentifier,
name: resolvedPackage.manifest.application.displayName,
logo: resolvedPackage.manifest.application.logoUrl ?? null,
workspaceId: params.workspaceId,
applicationRegistrationId: appRegistration.id,
sourceType: appRegistration.sourceType,
@@ -454,11 +457,15 @@ export class ApplicationInstallService {
);
}
// TODO: mimeType should be defined, default to application/octet-stream, which won't be displayed
// inline by the browser (forced download) due to Content-Disposition security headers.
const { mimeType, ext } = await extractFileInfo({
file: content,
filename: relativePath,
});
const sanitizedContent = sanitizeFile({ file: content, ext, mimeType });
await this.fileStorageService.writeFile({
sourceFile: content,
mimeType: undefined,
sourceFile: sanitizedContent,
mimeType,
fileFolder,
applicationUniversalIdentifier,
workspaceId,
@@ -506,6 +513,7 @@ export class ApplicationInstallService {
existingApplication: ApplicationEntity | null;
universalIdentifier: string;
name: string;
logo: string | null;
workspaceId: string;
applicationRegistrationId: string;
sourceType: ApplicationRegistrationSourceType;
@@ -517,6 +525,7 @@ export class ApplicationInstallService {
return await this.applicationService.create({
universalIdentifier: params.universalIdentifier,
name: params.name,
logo: params.logo,
sourcePath: params.universalIdentifier,
sourceType: params.sourceType,
applicationRegistrationId: params.applicationRegistrationId,
@@ -159,6 +159,7 @@ export class ApplicationSyncService {
return await this.applicationService.update(application.id, {
name,
description: manifest.application.description,
logo: manifest.application.logoUrl ?? null,
version: packageJson.version,
packageJsonChecksum: manifest.application.packageJsonChecksum,
yarnLockChecksum: manifest.application.yarnLockChecksum,
@@ -25,10 +25,6 @@ export class MarketplaceAppDTO {
@MaxLength(160)
description: string;
@IsString()
@Field()
icon: string;
@IsString()
@Field()
author: string;
@@ -87,7 +87,6 @@ export class MarketplaceQueryService {
id: registration.universalIdentifier,
name: app?.displayName ?? registration.name,
description: app?.description ?? '',
icon: app?.icon ?? 'IconApps',
author: `${app?.author ?? 'Unknown'}`,
category: app?.category ?? '',
logo: app?.logoUrl ?? undefined,
@@ -278,6 +278,14 @@ export class ApplicationRegistrationService {
params.universalIdentifier,
);
const curatedIdentifiers = new Set(
MARKETPLACE_CURATED_APPLICATIONS.map(
(entry) => entry.universalIdentifier,
),
);
const isFeatured = curatedIdentifiers.has(params.universalIdentifier);
if (isDefined(existing)) {
await this.applicationRegistrationRepository.save({
...existing,
@@ -286,16 +294,9 @@ export class ApplicationRegistrationService {
sourcePackage: params.sourcePackage,
latestAvailableVersion: params.latestAvailableVersion,
manifest: params.manifest,
isFeatured,
});
} else {
const curatedIdentifiers = new Set(
MARKETPLACE_CURATED_APPLICATIONS.map(
(entry) => entry.universalIdentifier,
),
);
const isFeatured = curatedIdentifiers.has(params.universalIdentifier);
const registration = this.applicationRegistrationRepository.create({
universalIdentifier: params.universalIdentifier,
name: params.name,
@@ -50,6 +50,9 @@ export class ApplicationEntity extends WorkspaceRelatedEntity {
@Column({ nullable: true, type: 'text' })
description: string | null;
@Column({ nullable: true, type: 'text' })
logo: string | null;
// TODO should not be nullable
@Column({ nullable: true, type: 'text' })
version: string | null;
@@ -34,6 +34,11 @@ export class ApplicationDTO {
@Field({ nullable: true })
description?: string;
@IsOptional()
@IsString()
@Field({ nullable: true })
logo?: string;
@IsOptional()
@IsString()
@Field({ nullable: true })
@@ -5,6 +5,7 @@ export const fromFlatApplicationToApplicationDto = ({
canBeUninstalled,
description,
id,
logo,
name,
packageJsonChecksum,
packageJsonFileId,
@@ -19,6 +20,7 @@ export const fromFlatApplicationToApplicationDto = ({
canBeUninstalled,
description: description ?? undefined,
id,
logo: logo ?? undefined,
name,
objects: [],
packageJsonChecksum: packageJsonChecksum ?? undefined,