Skip to content

Commit

Permalink
feat(global settings): can define a custom favicon (#503)
Browse files Browse the repository at this point in the history
  • Loading branch information
TdyP committed Jul 2, 2024
1 parent 3f1ba6d commit 7dd4831
Show file tree
Hide file tree
Showing 19 changed files with 179 additions and 44 deletions.
2 changes: 1 addition & 1 deletion apps/admin/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="/favicon-leav.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Powered by LEAV-Engine"
/>
<link rel="icon" href="../../global-favicon" />
<link rel="manifest" href="/manifest.json" crossorigin="use-credentials"/>
<title>LEAV Engine - Administration</title>
<script>window.__dynamic_base__ = `${window.location.origin}/app/${window.location.pathname.split('/').filter(e => e)[1]}`</script>
Expand Down
3 changes: 2 additions & 1 deletion apps/admin/public/locales/en/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@
"customization": {
"title": "Customization",
"name": "Name",
"icon": "Icon"
"icon": "Icon",
"favicon": "Favicon"
}
},
"errors": {
Expand Down
3 changes: 2 additions & 1 deletion apps/admin/public/locales/fr/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@
"customization": {
"title": "Personnalisation",
"name": "Nom",
"icon": "Icône"
"icon": "Icône",
"favicon": "Favicon"
}
},
"errors": {
Expand Down
18 changes: 18 additions & 0 deletions apps/admin/src/_gqlTypes/GET_GLOBAL_SETTINGS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,33 @@ export interface GET_GLOBAL_SETTINGS_globalSettings_icon_whoAmI {
color: string | null;
preview: Preview | null;
}
export interface GET_GLOBAL_SETTINGS_globalSettings_favicon_whoAmI_library {
id: string;
label: SystemTranslation | null;
}

export interface GET_GLOBAL_SETTINGS_globalSettings_favicon_whoAmI {
id: string;
library: GET_GLOBAL_SETTINGS_globalSettings_favicon_whoAmI_library;
label: string | null;
color: string | null;
preview: Preview | null;
}

export interface GET_GLOBAL_SETTINGS_globalSettings_icon {
id: string;
whoAmI: GET_GLOBAL_SETTINGS_globalSettings_icon_whoAmI;
}

export interface GET_GLOBAL_SETTINGS_globalSettings_favicon {
id: string;
whoAmI: GET_GLOBAL_SETTINGS_globalSettings_favicon_whoAmI;
}

export interface GET_GLOBAL_SETTINGS_globalSettings {
name: string;
icon: GET_GLOBAL_SETTINGS_globalSettings_icon | null;
favicon: GET_GLOBAL_SETTINGS_globalSettings_favicon | null;
}

export interface GET_GLOBAL_SETTINGS {
Expand Down
5 changes: 3 additions & 2 deletions apps/admin/src/_gqlTypes/globalTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -316,14 +316,15 @@ export interface FormInput {
elements?: FormElementsByDepsInput[] | null;
}

export interface GlobalSettingsIconInput {
export interface GlobalSettingsFileInput {
library: string;
recordId: string;
}

export interface GlobalSettingsInput {
name?: string | null;
icon?: GlobalSettingsIconInput | null;
icon?: GlobalSettingsFileInput | null;
favicon?: GlobalSettingsFileInput | null;
}

export interface LibraryIconInput {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@ function CustomizationForm({settings, onSubmit}: ICustomizationFormProps): JSX.E
setName(val);
};

const _handleFileSelection = (selectedFile: RecordIdentity_whoAmI) => {
const _handleFileSelection = (field: keyof Pick<GlobalSettingsInput, 'icon' | 'favicon'>) => (
selectedFile: RecordIdentity_whoAmI
) => {
onSubmit({
icon: selectedFile
[field]: selectedFile
? {
library: selectedFile.library.id,
recordId: selectedFile.id
Expand Down Expand Up @@ -65,13 +67,20 @@ function CustomizationForm({settings, onSubmit}: ICustomizationFormProps): JSX.E
onKeyPress={_handleKeyPress}
onChange={_handleChangeName}
/>
<Form.Field>
<Form.Field name="icon">
<FileSelector
onChange={_handleFileSelection}
onChange={_handleFileSelection('icon')}
value={settings?.icon?.whoAmI}
label={t('general.customization.icon')}
/>
</Form.Field>
<Form.Field name="favicon">
<FileSelector
onChange={_handleFileSelection('favicon')}
value={settings?.favicon?.whoAmI}
label={t('general.customization.favicon')}
/>
</Form.Field>
</Form>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,13 @@ import React from 'react';
import {render, screen, waitFor} from '_tests/testUtils';
import GeneralCustomizationTab from './GeneralCustomizationTab';

jest.mock('components/shared/FileSelector', () => function FileSelector() {
return <div>FileSelector</div>;
});
jest.mock(
'components/shared/FileSelector',
() =>
function FileSelector() {
return <div>FileSelector</div>;
}
);

describe('GeneralCustomizationTab', () => {
test('Render name and file selector', async () => {
Expand Down Expand Up @@ -58,7 +62,7 @@ describe('GeneralCustomizationTab', () => {
expect(await screen.findByRole('textbox', {name: 'name'})).toBeInTheDocument();

expect(screen.getByRole('textbox', {name: 'name'})).toHaveValue('My App');
expect(screen.getByText('FileSelector')).toBeInTheDocument();
expect(screen.getAllByText('FileSelector')).toHaveLength(2);

//Edit name and submit
userEvent.type(screen.getByRole('textbox', {name: 'name'}), ' Modified{Enter}');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ export const getGlobalSettingsQuery = gql`
id
...RecordIdentity
}
favicon {
id
...RecordIdentity
}
}
}
`;
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ export const saveGlobalSettingsQuery = gql`
id
...RecordIdentity
}
favicon {
id
...RecordIdentity
}
}
}
`;
4 changes: 4 additions & 0 deletions apps/core/src/__tests__/mocks/globalSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,9 @@ export const mockGlobalSettings: IGlobalSettings = {
icon: {
library: 'myLibraryId',
recordId: '42'
},
favicon: {
library: 'myLibraryId',
recordId: '1337'
}
};
4 changes: 4 additions & 0 deletions apps/core/src/_types/globalSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,8 @@ export interface IGlobalSettings {
library: string;
recordId: string;
};
favicon?: {
library: string;
recordId: string;
};
}
117 changes: 93 additions & 24 deletions apps/core/src/app/core/globalSettingsApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,23 +40,51 @@ export default function ({
'core.utils': utils = null,
config = null
}: IDeps): ICoreApp {
const _getFileRecord = async (
settings: IGlobalSettings,
field: keyof Pick<IGlobalSettings, 'icon' | 'favicon'>,
ctx: IQueryInfos
) => {
if (!settings[field]) {
return null;
}

const record = await recordDomain.find({
params: {
library: settings[field].library,
filters: [
{
field: 'id',
value: settings[field].recordId,
condition: AttributeCondition.EQUAL
}
]
},
ctx
});

return record.list.length ? record.list[0] : null;
};

return {
async getGraphQLSchema(): Promise<IAppGraphQLSchema> {
const baseSchema = {
typeDefs: `
type GlobalSettings {
name: String!,
icon: Record
icon: Record,
favicon: Record
}
input GlobalSettingsIconInput {
input GlobalSettingsFileInput {
library: String!,
recordId: String!
}
input GlobalSettingsInput {
name: String,
icon: GlobalSettingsIconInput
icon: GlobalSettingsFileInput
favicon: GlobalSettingsFileInput
}
extend type Query {
Expand All @@ -76,27 +104,10 @@ export default function ({

return settings.name;
},
icon: async (settings: IGlobalSettings, _, ctx: IQueryInfos) => {
if (!settings.icon) {
return null;
}

const record = await recordDomain.find({
params: {
library: settings.icon.library,
filters: [
{
field: 'id',
value: settings.icon.recordId,
condition: AttributeCondition.EQUAL
}
]
},
ctx
});

return record.list.length ? record.list[0] : null;
}
icon: async (settings: IGlobalSettings, _, ctx: IQueryInfos) =>
_getFileRecord(settings, 'icon', ctx),
favicon: async (settings: IGlobalSettings, _, ctx: IQueryInfos) =>
_getFileRecord(settings, 'favicon', ctx)
},
Query: {
globalSettings: async (_, args, ctx: IQueryInfos) => {
Expand Down Expand Up @@ -137,6 +148,12 @@ export default function ({
res.sendFile(defaultIconPath);
};

const _serveDefaultFavicon = async (req: IRequestWithContext, res: Response, next: NextFunction) => {
const rootPath = appRootPath();
const defaultFaviconPath = path.resolve(rootPath, '../../assets/favicon-leav.svg');
res.sendFile(defaultFaviconPath);
};

app.get(
'/global-name',
_initCtx,
Expand Down Expand Up @@ -216,6 +233,58 @@ export default function ({
},
_handleError
);

app.get(
'/global-favicon',
_initCtx,
async (req: IRequestWithContext, res: Response, next: NextFunction) => {
try {
const settings = await globalSettingsDomain.getSettings(req.ctx);

if (!settings.favicon) {
_serveDefaultFavicon(req, res, next);
return;
}

const fileRecord = (
await recordDomain.find({
params: {
library: settings.favicon.library,
filters: [
{
field: 'id',
value: settings.favicon.recordId,
condition: AttributeCondition.EQUAL
}
]
},
ctx: req.ctx
})
).list[0];

if (!fileRecord) {
_serveDefaultFavicon(req, res, next);
return;
}

const previewsAttribute = utils.getPreviewsAttributeName(settings.favicon.library);
let previewPath = fileRecord[previewsAttribute].tiny;
// Remove leading slash of previewPath
if (previewPath.startsWith('/')) {
previewPath = previewPath.slice(1);
}

const filePath = path.resolve(config.preview.directory, previewPath);

// Cache TTL is 1 day
res.set('Cache-Control', 'public, max-age=86400');
res.sendFile(filePath);
} catch (err) {
return next(err);
}
},
_handleError
);
}
};
}
3 changes: 2 additions & 1 deletion apps/core/src/domain/globalSettings/globalSettingsDomain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ export default function ({

return {
name: settings.name,
icon: settings.icon
icon: settings.icon,
favicon: settings.favicon
};
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ exports[`getSettingsRepo saveSettings Should save settings 2`] = `
"value0": "1",
"value1": {
"_key": "1",
"favicon": {
"library": "myLibraryId",
"recordId": "1337",
},
"icon": {
"library": "myLibraryId",
"recordId": "42",
Expand Down
8 changes: 5 additions & 3 deletions apps/core/src/infra/globalSettings/globalSettingsRepo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ interface IDeps {

const GLOBAL_SETTINGS_COLLECTION = 'core_global_settings';

export default function({'core.infra.db.dbService': dbService = null}: IDeps = {}): IGlobalSettingsRepo {
export default function ({'core.infra.db.dbService': dbService = null}: IDeps = {}): IGlobalSettingsRepo {
const settingsKey = '1';
return {
async saveSettings({settings, ctx}) {
Expand All @@ -36,7 +36,8 @@ export default function({'core.infra.db.dbService': dbService = null}: IDeps = {

return {
name: savedSettings?.[0]?.name ?? null,
icon: savedSettings?.[0]?.icon ?? null
icon: savedSettings?.[0]?.icon ?? null,
favicon: savedSettings?.[0]?.favicon ?? null
};
},
async getSettings(ctx) {
Expand All @@ -53,7 +54,8 @@ export default function({'core.infra.db.dbService': dbService = null}: IDeps = {

return {
name: settings?.[0]?.name ?? null,
icon: settings?.[0]?.icon ?? null
icon: settings?.[0]?.icon ?? null,
favicon: settings?.[0]?.favicon ?? null
};
}
};
Expand Down
Loading

0 comments on commit 7dd4831

Please sign in to comment.