Skip to content

Commit bccc722

Browse files
authored
fix(chat): use hostname brand icons for inline sources (#8350)
* fix(chat): use hostname brand icons for inline sources * fix(chat): recognize www aliases in source branding
1 parent eeaa57b commit bccc722

2 files changed

Lines changed: 49 additions & 1 deletion

File tree

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/** @vitest-environment jsdom */
2+
import { renderToStaticMarkup } from 'react-dom/server'
3+
import { describe, expect, it } from 'vitest'
4+
import { SourceIcon } from '@/app/workspace/[workspaceId]/home/components/message-content/components/source-chip/source-icon'
5+
6+
describe('source hostname branding', () => {
7+
it.each([
8+
'https://example.slack.com/archives/channel/message',
9+
'https://app.slack.com/client/team/channel',
10+
'https://SLACK.COM/help',
11+
'https://www.github.com/example/project',
12+
'https://www.gitlab.com/example/project',
13+
'https://www.notion.so/example',
14+
])('renders a local brand instead of requesting a favicon for a recognized host: %s', (url) => {
15+
const container = document.createElement('div')
16+
container.innerHTML = renderToStaticMarkup(<SourceIcon source={{ url }} />)
17+
expect(container.querySelector('img')).toBeNull()
18+
expect(container.querySelector('svg')).not.toBeNull()
19+
})
20+
21+
it.each([
22+
'https://notslack.com/channel',
23+
'https://slack.com.example.org/channel',
24+
'https://slack.com@example.org/channel',
25+
'https://example.org/slack.com',
26+
'https://example.org/?url=https://example.slack.com',
27+
])('retains the actual destination favicon for a lookalike URL: %s', (url) => {
28+
const container = document.createElement('div')
29+
container.innerHTML = renderToStaticMarkup(<SourceIcon source={{ url }} />)
30+
const image = container.querySelector('img')
31+
expect(image).not.toBeNull()
32+
expect(new URL(image!.src).searchParams.get('domain')).toBe(new URL(url).hostname)
33+
})
34+
})

‎apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/source-chip/source-icon.tsx‎

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,15 @@ const GOOGLE_DOCUMENT_ICON_BY_PATH: ReadonlyMap<string, StyleableIcon> = new Map
2929
['presentation', GoogleSlidesIcon],
3030
])
3131

32+
const BRAND_TYPE_BY_HOSTNAME: ReadonlyMap<string, string> = new Map([
33+
['github.com', 'github'],
34+
['gitlab.com', 'gitlab'],
35+
['notion.so', 'notion'],
36+
['drive.google.com', 'google_drive'],
37+
['mail.google.com', 'gmail'],
38+
['calendar.google.com', 'google_calendar'],
39+
])
40+
3241
interface SourceIconProps {
3342
source: SourceTagData
3443
size?: 'default' | 'inline'
@@ -45,7 +54,12 @@ export function SourceIcon({ source, size = 'default' }: SourceIconProps) {
4554
hostname === 'docs.google.com'
4655
? GOOGLE_DOCUMENT_ICON_BY_PATH.get(new URL(source.url).pathname.split('/')[1] ?? '')
4756
: undefined
48-
const Icon = DocumentIcon ?? ConnectorIcon
57+
const hostBrandType =
58+
hostname === 'slack.com' || hostname?.endsWith('.slack.com')
59+
? 'slack'
60+
: BRAND_TYPE_BY_HOSTNAME.get(hostname?.replace(/^www\./, '') ?? '')
61+
const HostIcon = hostBrandType ? BRAND_ICON_BY_BASE_TYPE.get(hostBrandType) : undefined
62+
const Icon = DocumentIcon ?? ConnectorIcon ?? HostIcon
4963
const className = cn('shrink-0', size === 'inline' ? 'size-[12px]' : 'size-[14px]')
5064
if (Icon) return <BrandIcon icon={Icon} className={className} />
5165
if (hostname && failedHostname !== hostname) {

0 commit comments

Comments
 (0)