Skip to content

Commit bd75254

Browse files
authored
feat(workspace): support GIF logos (#7322)
1 parent 8309ad5 commit bd75254

5 files changed

Lines changed: 65 additions & 15 deletions

File tree

apps/sim/app/workspace/[workspaceId]/w/components/sidebar/hooks/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ export { useWorkflowOperations } from './use-workflow-operations'
1818
export { useWorkflowSelection } from './use-workflow-selection'
1919
export { useWorkspaceLogoUpload } from './use-workspace-logo-upload'
2020
export { useWorkspaceManagement } from './use-workspace-management'
21+
export { WORKSPACE_LOGO_ACCEPT_ATTRIBUTE } from './workspace-logo-file'

apps/sim/app/workspace/[workspaceId]/w/components/sidebar/hooks/use-workspace-logo-upload.ts

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@ import { useCallback, useEffect, useRef, useState } from 'react'
22
import { createLogger } from '@sim/logger'
33
import { getErrorMessage } from '@sim/utils/errors'
44
import { uploadInternalFileSession } from '@/lib/uploads/client/session-upload'
5+
import { validateWorkspaceLogoFile } from '@/app/workspace/[workspaceId]/w/components/sidebar/hooks/workspace-logo-file'
56

67
const logger = createLogger('WorkspaceLogoUpload')
7-
const MAX_FILE_SIZE = 5 * 1024 * 1024 // 5MB
8-
const ACCEPTED_IMAGE_TYPES = ['image/png', 'image/jpeg', 'image/jpg', 'image/svg+xml', 'image/webp']
98

109
interface UseWorkspaceLogoUploadProps {
1110
workspaceId?: string
@@ -51,16 +50,6 @@ export function useWorkspaceLogoUpload({
5150
setPreviewUrl(currentLogoUrl || null)
5251
}, [currentLogoUrl])
5352

54-
const validateFile = useCallback((file: File): string | null => {
55-
if (file.size > MAX_FILE_SIZE) {
56-
return `File "${file.name}" is too large. Maximum size is 5MB.`
57-
}
58-
if (!ACCEPTED_IMAGE_TYPES.includes(file.type)) {
59-
return `File "${file.name}" is not a supported image format. Please use PNG, JPEG, SVG, or WebP.`
60-
}
61-
return null
62-
}, [])
63-
6453
const uploadFileToServer = useCallback(async (file: File): Promise<string> => {
6554
const targetWorkspaceId = workspaceIdRef.current
6655
if (!targetWorkspaceId) {
@@ -78,7 +67,7 @@ export function useWorkspaceLogoUpload({
7867

7968
const processFile = useCallback(
8069
async (file: File) => {
81-
const validationError = validateFile(file)
70+
const validationError = validateWorkspaceLogoFile(file)
8271
if (validationError) {
8372
onErrorRef.current?.(validationError)
8473
return
@@ -106,7 +95,7 @@ export function useWorkspaceLogoUpload({
10695
setIsUploading(false)
10796
}
10897
},
109-
[uploadFileToServer, validateFile]
98+
[uploadFileToServer]
11099
)
111100

112101
const handleFileChange = useCallback(
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { describe, expect, it } from 'vitest'
2+
import {
3+
validateWorkspaceLogoFile,
4+
WORKSPACE_LOGO_ACCEPT_ATTRIBUTE,
5+
} from '@/app/workspace/[workspaceId]/w/components/sidebar/hooks/workspace-logo-file'
6+
7+
function file(overrides: Partial<Pick<File, 'name' | 'size' | 'type'>> = {}) {
8+
return {
9+
name: 'logo.png',
10+
size: 1024,
11+
type: 'image/png',
12+
...overrides,
13+
}
14+
}
15+
16+
describe('workspace logo files', () => {
17+
it('advertises and accepts GIF images', () => {
18+
expect(WORKSPACE_LOGO_ACCEPT_ATTRIBUTE.split(',')).toContain('image/gif')
19+
expect(validateWorkspaceLogoFile(file({ name: 'animated.gif', type: 'image/gif' }))).toBeNull()
20+
})
21+
22+
it('rejects files larger than 5MB', () => {
23+
expect(validateWorkspaceLogoFile(file({ size: 5 * 1024 * 1024 + 1 }))).toBe(
24+
'File "logo.png" is too large. Maximum size is 5MB.'
25+
)
26+
})
27+
28+
it('lists GIF among the supported formats in validation errors', () => {
29+
expect(validateWorkspaceLogoFile(file({ name: 'logo.bmp', type: 'image/bmp' }))).toBe(
30+
'File "logo.bmp" is not a supported image format. Please use PNG, JPEG, GIF, SVG, or WebP.'
31+
)
32+
})
33+
})
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
const MAX_WORKSPACE_LOGO_SIZE = 5 * 1024 * 1024
2+
3+
const WORKSPACE_LOGO_IMAGE_TYPES = [
4+
'image/png',
5+
'image/jpeg',
6+
'image/jpg',
7+
'image/gif',
8+
'image/svg+xml',
9+
'image/webp',
10+
] as const
11+
12+
const WORKSPACE_LOGO_IMAGE_TYPE_SET = new Set<string>(WORKSPACE_LOGO_IMAGE_TYPES)
13+
14+
export const WORKSPACE_LOGO_ACCEPT_ATTRIBUTE = WORKSPACE_LOGO_IMAGE_TYPES.join(',')
15+
16+
export function validateWorkspaceLogoFile(
17+
file: Pick<File, 'name' | 'size' | 'type'>
18+
): string | null {
19+
if (file.size > MAX_WORKSPACE_LOGO_SIZE) {
20+
return `File "${file.name}" is too large. Maximum size is 5MB.`
21+
}
22+
if (!WORKSPACE_LOGO_IMAGE_TYPE_SET.has(file.type)) {
23+
return `File "${file.name}" is not a supported image format. Please use PNG, JPEG, GIF, SVG, or WebP.`
24+
}
25+
return null
26+
}

apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ import {
9393
useWorkflowOperations,
9494
useWorkspaceLogoUpload,
9595
useWorkspaceManagement,
96+
WORKSPACE_LOGO_ACCEPT_ATTRIBUTE,
9697
} from '@/app/workspace/[workspaceId]/w/components/sidebar/hooks'
9798
import {
9899
compareByOrder,
@@ -1330,7 +1331,7 @@ export const Sidebar = memo(function Sidebar({
13301331
<input
13311332
ref={logoFileInputRef}
13321333
type='file'
1333-
accept='image/png,image/jpeg,image/jpg,image/svg+xml,image/webp'
1334+
accept={WORKSPACE_LOGO_ACCEPT_ATTRIBUTE}
13341335
className='hidden'
13351336
onChange={handleLogoFileChange}
13361337
/>

0 commit comments

Comments
 (0)