mirror of
https://github.com/twentyhq/twenty.git
synced 2024-12-18 09:02:11 +03:00
Sanitize url before fetching favicon and display letter avatar if it can't be retrieved (#1035)
* Sanitize url before fetching favicon and display letter avatar if it can't be retrieved Co-authored-by: v1b3m <vibenjamin6@gmail.com> Co-authored-by: RubensRafael <rubensrafael2@live.com> * Priorotise www for apple.com domain Co-authored-by: v1b3m <vibenjamin6@gmail.com> Co-authored-by: RubensRafael <rubensrafael2@live.com> * Add requested changes Co-authored-by: v1b3m <vibenjamin6@gmail.com> Co-authored-by: RubensRafael <rubensrafael2@live.com> * Fix the tests Co-authored-by: v1b3m <vibenjamin6@gmail.com> Co-authored-by: RubensRafael <rubensrafael2@live.com> * Change avatar generation strategy Co-authored-by: v1b3m <vibenjamin6@gmail.com> Co-authored-by: RubensRafael <rubensrafael2@live.com> --------- Co-authored-by: v1b3m <vibenjamin6@gmail.com> Co-authored-by: RubensRafael <rubensrafael2@live.com>
This commit is contained in:
parent
bfd748e175
commit
2680289ff7
@ -8,6 +8,7 @@ import {
|
||||
ViewFieldDefinition,
|
||||
ViewFieldURLMetadata,
|
||||
} from '@/ui/table/types/ViewField';
|
||||
import { sanitizeURL } from '~/utils';
|
||||
|
||||
import { GenericEditableURLCellEditMode } from './GenericEditableURLCellEditMode';
|
||||
|
||||
@ -33,7 +34,9 @@ export function GenericEditableURLCell({
|
||||
<EditableCell
|
||||
editModeHorizontalAlign={editModeHorizontalAlign}
|
||||
editModeContent={<GenericEditableURLCellEditMode viewField={viewField} />}
|
||||
nonEditModeContent={<InplaceInputURLDisplayMode value={fieldValue} />}
|
||||
nonEditModeContent={
|
||||
<InplaceInputURLDisplayMode value={sanitizeURL(fieldValue)} />
|
||||
}
|
||||
></EditableCell>
|
||||
);
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { isNonEmptyString } from '~/utils/isNonEmptyString';
|
||||
@ -86,6 +87,20 @@ export function Avatar({
|
||||
type = 'squared',
|
||||
}: OwnProps) {
|
||||
const noAvatarUrl = !isNonEmptyString(avatarUrl);
|
||||
const [isInvalidAvatarUrl, setIsInvalidAvatarUrl] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (avatarUrl) {
|
||||
new Promise((resolve) => {
|
||||
const img = new Image();
|
||||
img.onload = () => resolve(false);
|
||||
img.onerror = () => resolve(true);
|
||||
img.src = getImageAbsoluteURIOrBase64(avatarUrl) as string;
|
||||
}).then((res) => {
|
||||
setIsInvalidAvatarUrl(res as boolean);
|
||||
});
|
||||
}
|
||||
}, [avatarUrl]);
|
||||
|
||||
return (
|
||||
<StyledAvatar
|
||||
@ -95,7 +110,8 @@ export function Avatar({
|
||||
type={type}
|
||||
colorId={colorId}
|
||||
>
|
||||
{noAvatarUrl && placeholder[0]?.toLocaleUpperCase()}
|
||||
{(noAvatarUrl || isInvalidAvatarUrl) &&
|
||||
placeholder[0]?.toLocaleUpperCase()}
|
||||
</StyledAvatar>
|
||||
);
|
||||
}
|
||||
|
@ -1,21 +1,49 @@
|
||||
import { getLogoUrlFromDomainName } from '..';
|
||||
import { getLogoUrlFromDomainName, sanitizeURL } from '..';
|
||||
|
||||
describe('sanitizeURL', () => {
|
||||
test('should sanitize the URL correctly', () => {
|
||||
expect(sanitizeURL('http://example.com/')).toBe('example.com');
|
||||
expect(sanitizeURL('https://www.example.com/')).toBe('example.com');
|
||||
expect(sanitizeURL('www.example.com')).toBe('example.com');
|
||||
expect(sanitizeURL('example.com')).toBe('example.com');
|
||||
expect(sanitizeURL('example.com/')).toBe('example.com');
|
||||
});
|
||||
|
||||
test('should handle undefined input', () => {
|
||||
expect(sanitizeURL(undefined)).toBe('');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getLogoUrlFromDomainName', () => {
|
||||
it(`should generate logo url if undefined `, () => {
|
||||
test('should return the correct logo URL for a given domain', () => {
|
||||
expect(getLogoUrlFromDomainName('example.com')).toBe(
|
||||
'https://favicon.twenty.com/example.com',
|
||||
);
|
||||
|
||||
expect(getLogoUrlFromDomainName('http://example.com/')).toBe(
|
||||
'https://favicon.twenty.com/example.com',
|
||||
);
|
||||
|
||||
expect(getLogoUrlFromDomainName('https://www.example.com/')).toBe(
|
||||
'https://favicon.twenty.com/example.com',
|
||||
);
|
||||
|
||||
expect(getLogoUrlFromDomainName('www.example.com')).toBe(
|
||||
'https://favicon.twenty.com/example.com',
|
||||
);
|
||||
|
||||
expect(getLogoUrlFromDomainName('example.com/')).toBe(
|
||||
'https://favicon.twenty.com/example.com',
|
||||
);
|
||||
|
||||
expect(getLogoUrlFromDomainName('apple.com')).toBe(
|
||||
'https://favicon.twenty.com/apple.com',
|
||||
);
|
||||
});
|
||||
|
||||
test('should handle undefined input', () => {
|
||||
expect(getLogoUrlFromDomainName(undefined)).toBe(
|
||||
'https://api.faviconkit.com/undefined/144',
|
||||
);
|
||||
});
|
||||
|
||||
it(`should generate logo url if defined `, () => {
|
||||
expect(getLogoUrlFromDomainName('test.com')).toBe(
|
||||
'https://api.faviconkit.com/test.com/144',
|
||||
);
|
||||
});
|
||||
|
||||
it(`should generate logo url if empty `, () => {
|
||||
expect(getLogoUrlFromDomainName('')).toBe(
|
||||
'https://api.faviconkit.com//144',
|
||||
'https://favicon.twenty.com/',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -10,6 +10,15 @@ export function formatToHumanReadableDate(date: Date | string) {
|
||||
}).format(parsedJSDate);
|
||||
}
|
||||
|
||||
export const getLogoUrlFromDomainName = (domainName?: string): string => {
|
||||
return `https://api.faviconkit.com/${domainName}/144`;
|
||||
};
|
||||
export function sanitizeURL(link: string | null | undefined) {
|
||||
return link
|
||||
? link.replace(/(https?:\/\/)|(www\.)/g, '').replace(/\/$/, '')
|
||||
: '';
|
||||
}
|
||||
|
||||
export function getLogoUrlFromDomainName(
|
||||
domainName?: string,
|
||||
): string | undefined {
|
||||
const sanitizedDomain = sanitizeURL(domainName);
|
||||
return `https://favicon.twenty.com/${sanitizedDomain}`;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user