diff --git a/front/src/modules/ui/table/editable-cell/type/components/GenericEditableURLCell.tsx b/front/src/modules/ui/table/editable-cell/type/components/GenericEditableURLCell.tsx index aadf2f4cfd..7a080f7811 100644 --- a/front/src/modules/ui/table/editable-cell/type/components/GenericEditableURLCell.tsx +++ b/front/src/modules/ui/table/editable-cell/type/components/GenericEditableURLCell.tsx @@ -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({ } - nonEditModeContent={} + nonEditModeContent={ + + } > ); } diff --git a/front/src/modules/users/components/Avatar.tsx b/front/src/modules/users/components/Avatar.tsx index cf7b9ab473..25718306c2 100644 --- a/front/src/modules/users/components/Avatar.tsx +++ b/front/src/modules/users/components/Avatar.tsx @@ -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 ( - {noAvatarUrl && placeholder[0]?.toLocaleUpperCase()} + {(noAvatarUrl || isInvalidAvatarUrl) && + placeholder[0]?.toLocaleUpperCase()} ); } diff --git a/front/src/utils/__tests__/utils.test.ts b/front/src/utils/__tests__/utils.test.ts index 8ac6b54752..9fa996f498 100644 --- a/front/src/utils/__tests__/utils.test.ts +++ b/front/src/utils/__tests__/utils.test.ts @@ -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/', ); }); }); diff --git a/front/src/utils/index.ts b/front/src/utils/index.ts index fcab4d1e0b..ed5a6745c7 100644 --- a/front/src/utils/index.ts +++ b/front/src/utils/index.ts @@ -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}`; +}