942755d0dd
## Problem
After installing an app, its icon is missing across the UI, while
application *registration* icons render fine.
`Application.logo` holds the manifest path (`public/logo.svg`), which is
package-relative and not displayable. The server exposes a `logoUrl`
resolve field that turns it into
`/public-assets/{workspaceId}/{applicationId}/{logo}`, but on the front
end:
- `APPLICATION_FRAGMENT` and `FIND_MANY_APPLICATIONS` never selected
`Application.logoUrl`.
- So the only source of a usable logo url was
`currentWorkspace.installedApplications`, which is fetched by
`GetCurrentUser` at bootstrap. Nothing refreshed it after
`installApplication`, so a freshly installed app was absent from that
list.
- `useApplicationChipData` then fell through to
`fallbackApplicationData`, which callers populated with the raw `logo`
path. `getAbsoluteImageUrl('public/logo.svg')` yields
`{serverUrl}/public/logo.svg`, which 404s, so the avatar rendered as a
letter placeholder.
## Before / After
An app installed while the applications page is open, so the workspace
snapshot loaded at bootstrap does not know about it yet:
| Before | After |
|---|---|
| <img
src="https://raw.githubusercontent.com/twentyhq/twenty/e27a817fd95c5e44c1fbb64fd4fd68525daeee61/.pr-assets/app-icon-before.png"
width="480"> | <img
src="https://raw.githubusercontent.com/twentyhq/twenty/e27a817fd95c5e44c1fbb64fd4fd68525daeee61/.pr-assets/app-icon-after.png"
width="480"> |
## Changes
- Select `logoUrl` on `Application` in `APPLICATION_FRAGMENT` and
`FIND_MANY_APPLICATIONS`.
- Drop `logo` from `ApplicationDisplayData` and from the `AppChip` /
subtable fallback props, so a package-relative path can no longer reach
an `img` src. Call sites that already passed a url under `logo` now pass
`logoUrl`.
- `SettingsApplicationDetails` and `SettingsApplicationsTable` pass the
application's own `logoUrl`.
- On install, add the returned application to
`currentWorkspace.installedApplications` instead of reloading the
current user, so the chips that resolve by `applicationId` only (nav
menu items, object/field tables, tool rows, workflow nodes) pick it up.
- Stop exposing `logo` on the `Application` GraphQL type: nothing
selects it anymore, and having both `logo` (package-relative path) and
`logoUrl` (display url) was the source of the bug. The column is still
read server-side to build `logoUrl`.
- Regenerated `generated-metadata/graphql.ts`.
## Verification
Ran the stack locally against a seeded workspace with an installed app
whose logo lives at `public/logo.png`:
- `findManyApplications` returns a `logoUrl` under `/public-assets/...`,
and that url serves `200 image/png`.
- Reproduced the bug and the fix in the browser with the scenario shown
above (screenshots taken on the base commit and on this branch).
- `npx nx typecheck twenty-front`, `npx nx typecheck twenty-server`,
`npx nx lint:diff-with-main` on both, and the application settings jest
suites pass.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01N8r4z2dZ553nAnCNe7GxMH)_
[Review in
cubic](https://cubic.dev/pr/twentyhq/twenty/pull/23411?utm_source=github)
60 lines
1.6 KiB
TypeScript
60 lines
1.6 KiB
TypeScript
import { makeMetadataAPIRequest } from 'test/integration/metadata/suites/utils/make-metadata-api-request.util';
|
|
import { type CommonResponseBody } from 'test/integration/metadata/types/common-response-body.type';
|
|
import { warnIfErrorButNotExpectedToFail } from 'test/integration/metadata/utils/warn-if-error-but-not-expected-to-fail.util';
|
|
import { warnIfNoErrorButExpectedToFail } from 'test/integration/metadata/utils/warn-if-no-error-but-expected-to-fail.util';
|
|
import gql from 'graphql-tag';
|
|
|
|
import { type ApplicationDTO } from 'src/engine/core-modules/application/dtos/application.dto';
|
|
|
|
export const APPLICATION_GQL_FIELDS = `
|
|
id
|
|
name
|
|
description
|
|
logoUrl
|
|
version
|
|
universalIdentifier
|
|
canBeUninstalled
|
|
`;
|
|
|
|
export const findManyApplications = async ({
|
|
gqlFields = APPLICATION_GQL_FIELDS,
|
|
expectToFail,
|
|
accessToken,
|
|
}: {
|
|
gqlFields?: string;
|
|
expectToFail?: boolean;
|
|
accessToken?: string;
|
|
}): CommonResponseBody<{
|
|
findManyApplications: ApplicationDTO[];
|
|
}> => {
|
|
const response = await makeMetadataAPIRequest(
|
|
{
|
|
query: gql`
|
|
query FindManyApplications {
|
|
findManyApplications {
|
|
${gqlFields}
|
|
}
|
|
}
|
|
`,
|
|
variables: {},
|
|
},
|
|
accessToken,
|
|
);
|
|
|
|
if (expectToFail === true) {
|
|
warnIfNoErrorButExpectedToFail({
|
|
response,
|
|
errorMessage: 'Application search should have failed but did not',
|
|
});
|
|
}
|
|
|
|
if (expectToFail === false) {
|
|
warnIfErrorButNotExpectedToFail({
|
|
response,
|
|
errorMessage: 'Application search has failed but should not',
|
|
});
|
|
}
|
|
|
|
return { data: response.body.data, errors: response.body.errors };
|
|
};
|