Skip to content

Commit 33a46b0

Browse files
authored
chore: fix file url (#191)
* chore: fix file url * chore: fix test * fix: test * fix: test
1 parent 9b98a2b commit 33a46b0

File tree

15 files changed

+170
-77
lines changed

15 files changed

+170
-77
lines changed

src/application/services/js-services/http/__tests__/setup.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,11 @@ jest.mock('@/utils/runtime-config', () => ({
8989
}));
9090

9191
jest.mock('@/utils/file-storage-url', () => ({
92-
getFileStorageUrl: jest.fn((url: string) => url),
93-
getFileUploadUrl: jest.fn((url: string) => url),
92+
getAppFlowyFileUrl: jest.fn((workspaceId: string, viewId: string, fileId: string) => `mock://${workspaceId}/${viewId}/${fileId}`),
93+
getAppFlowyFileUploadUrl: jest.fn((workspaceId: string, viewId: string) => `mock://${workspaceId}/${viewId}/upload`),
94+
resolveFileUrl: jest.fn((urlOrId: string) => urlOrId),
95+
isFileURL: jest.fn(() => true),
96+
isAppFlowyFileStorageUrl: jest.fn(() => true),
9497
}));
9598

9699
// Mock the session/token module (not @/application/session!)
@@ -188,7 +191,7 @@ export class AuthHelper {
188191
refreshToken,
189192
user,
190193
};
191-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
194+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
192195
} catch (error: any) {
193196
// If signup fails (user exists), try to sign in
194197
if (error.response?.status === 422 || error.response?.status === 400) {

src/application/services/js-services/http/http_api.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { omit } from 'lodash-es';
44
import { nanoid } from 'nanoid';
55

66
import { GlobalComment, Reaction } from '@/application/comment.type';
7-
import { Log } from '@/utils/log';
87
import { ERROR_CODE } from '@/application/constants';
98
import { initGrantService, refreshToken } from '@/application/services/js-services/http/gotrue';
109
import { parseGoTrueErrorFromUrl } from '@/application/services/js-services/http/gotrue-error';
@@ -66,7 +65,8 @@ import {
6665
} from '@/application/types';
6766
import { notify } from '@/components/_shared/notify';
6867
import { RepeatedChatMessage } from '@/components/chat';
69-
import { getFileUploadUrl, getFileUrl } from '@/utils/file-storage-url';
68+
import { getAppFlowyFileUploadUrl, getAppFlowyFileUrl } from '@/utils/file-storage-url';
69+
import { Log } from '@/utils/log';
7070

7171
export * from './gotrue';
7272

@@ -1552,7 +1552,7 @@ export async function uploadFile(
15521552
file: File,
15531553
onProgress?: (progress: number) => void
15541554
) {
1555-
const url = getFileUploadUrl(workspaceId, viewId);
1555+
const url = getAppFlowyFileUploadUrl(workspaceId, viewId);
15561556

15571557
// Check file size, if over 7MB, check subscription plan
15581558
if (file.size > 7 * 1024 * 1024) {
@@ -1587,7 +1587,7 @@ export async function uploadFile(
15871587
});
15881588

15891589
if (response?.data.code === 0) {
1590-
return getFileUrl(workspaceId, viewId, response?.data.data.file_id);
1590+
return getAppFlowyFileUrl(workspaceId, viewId, response?.data.data.file_id);
15911591
}
15921592

15931593
return Promise.reject(response?.data);

src/components/_shared/gallery-preview/GalleryPreview.tsx

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { ReactComponent as ReloadIcon } from '@/assets/icons/reset.svg';
1212
import { ReactComponent as DownloadIcon } from '@/assets/icons/save_as.svg';
1313
import { notify } from '@/components/_shared/notify';
1414
import { copyTextToClipboard } from '@/utils/copy';
15-
import { getFileUrl, isFileURL } from '@/utils/file-storage-url';
15+
import { resolveFileUrl } from '@/utils/file-storage-url';
1616

1717
export interface GalleryImage {
1818
src: string;
@@ -96,13 +96,7 @@ function GalleryPreview({ images, open, onClose, previewIndex, workspaceId, view
9696
}, [handleKeydown]);
9797

9898
const imageUrl = useMemo(() => {
99-
if (isFileURL(images[index].src)) {
100-
return images[index].src;
101-
}
102-
103-
const fileId = images[index].src;
104-
105-
return getFileUrl(workspaceId, viewId, fileId);
99+
return resolveFileUrl(images[index].src, workspaceId, viewId);
106100
}, [images, index, workspaceId, viewId]);
107101

108102
if (!open) {

src/components/_shared/hooks/useAuthenticatedImage.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { useEffect, useRef, useState } from 'react';
22

33
import { getImageUrl, revokeBlobUrl } from '@/utils/authenticated-image';
4+
import { Log } from '@/utils/log';
45

56
/**
67
* Hook to handle authenticated image loading for AppFlowy file storage URLs
@@ -21,6 +22,7 @@ export function useAuthenticatedImage(src: string | undefined): string {
2122

2223
let isMounted = true;
2324

25+
Log.debug('[useAuthenticatedImage] src', src);
2426
getImageUrl(src)
2527
.then((url) => {
2628
if (isMounted) {

src/components/database/components/cell/file-media/PreviewImage.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,15 @@ import { useMemo } from 'react';
33
import { useDatabaseContext } from '@/application/database-yjs';
44
import { FileMediaCellDataItem } from '@/application/database-yjs/cell.type';
55
import { useAuthenticatedImage } from '@/components/_shared/hooks/useAuthenticatedImage';
6-
import { getFileUrl, isFileURL } from '@/utils/file-storage-url';
6+
import { resolveFileUrl } from '@/utils/file-storage-url';
77

88
function PreviewImage({ file, onClick }: { file: FileMediaCellDataItem; onClick: () => void }) {
99
const { workspaceId, databasePageId } = useDatabaseContext();
1010

1111
const thumb = useMemo(() => {
12-
let fileUrl = file.url;
12+
const fileUrl = resolveFileUrl(file.url, workspaceId, databasePageId);
1313

1414
if (!fileUrl) return '';
15-
if (!isFileURL(fileUrl)) {
16-
fileUrl = getFileUrl(workspaceId, databasePageId, file.url);
17-
}
1815

1916
const url = new URL(fileUrl);
2017

src/components/database/components/cell/file-media/UnPreviewFile.tsx

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { FileMediaCellDataItem } from '@/application/database-yjs/cell.type';
44
import FileIcon from '@/components/database/components/cell/file-media/FileIcon';
55
import { Button } from '@/components/ui/button';
66
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
7-
import { getFileUrl, isFileURL } from '@/utils/file-storage-url';
7+
import { resolveFileUrl } from '@/utils/file-storage-url';
88
import { openUrl } from '@/utils/url';
99

1010
function UnPreviewFile({ file }: { file: FileMediaCellDataItem }) {
@@ -19,15 +19,11 @@ function UnPreviewFile({ file }: { file: FileMediaCellDataItem }) {
1919
className={'cursor-pointer rounded-[4px] bg-fill-content-hover text-icon-secondary'}
2020
onClick={(e) => {
2121
e.stopPropagation();
22-
if (file.url && isFileURL(file.url)) {
23-
void openUrl(file.url, '_blank');
24-
return;
25-
}
26-
27-
const fileId = file.url;
28-
const newUrl = getFileUrl(workspaceId, databasePageId, fileId);
22+
const newUrl = resolveFileUrl(file.url, workspaceId, databasePageId);
2923

30-
void openUrl(newUrl, '_blank');
24+
if (newUrl) {
25+
void openUrl(newUrl, '_blank');
26+
}
3127
}}
3228
>
3329
<FileIcon fileType={file.file_type} />

src/components/database/components/database-row/file-media/FileMediaItem.tsx

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import FileIcon from '@/components/database/components/cell/file-media/FileIcon'
77
import FileMediaMore from '@/components/database/components/cell/file-media/FileMediaMore';
88
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
99
import { cn } from '@/lib/utils';
10-
import { getFileUrl, isFileURL } from '@/utils/file-storage-url';
10+
import { resolveFileUrl } from '@/utils/file-storage-url';
1111
import { openUrl } from '@/utils/url';
1212

1313
function FileMediaItem({
@@ -48,13 +48,7 @@ function FileMediaItem({
4848
}, [file.file_type]);
4949

5050
const fileUrl = useMemo(() => {
51-
if (file.url && isFileURL(file.url)) {
52-
return file.url;
53-
}
54-
55-
const fileId = file.url;
56-
57-
return getFileUrl(workspaceId, databasePageId, fileId);
51+
return resolveFileUrl(file.url, workspaceId, databasePageId);
5852
}, [file.url, workspaceId, databasePageId]);
5953

6054
const authenticatedFileUrl = useAuthenticatedImage(fileUrl);
@@ -67,15 +61,11 @@ function FileMediaItem({
6761
e.stopPropagation();
6862
// Open the file in a new tab
6963
if (file.file_type !== FileMediaType.Image) {
70-
if (file.url && isFileURL(file.url)) {
71-
void openUrl(file.url, '_blank');
72-
return;
73-
}
74-
75-
const fileId = file.url;
76-
const newUrl = getFileUrl(workspaceId, databasePageId, fileId);
64+
const newUrl = resolveFileUrl(file.url, workspaceId, databasePageId);
7765

78-
void openUrl(newUrl, '_blank');
66+
if (newUrl) {
67+
void openUrl(newUrl, '_blank');
68+
}
7969
}
8070
}}
8171
onMouseEnter={() => setHover(true)}

src/components/editor/components/blocks/gallery/GalleryBlock.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import ImageGallery from '@/components/editor/components/blocks/gallery/ImageGal
1212
import { EditorElementProps, GalleryBlockNode } from '@/components/editor/editor.type';
1313
import { useEditorContext } from '@/components/editor/EditorContext';
1414
import { copyTextToClipboard } from '@/utils/copy';
15-
import { getFileUrl, isFileURL } from '@/utils/file-storage-url';
15+
import { resolveFileUrl } from '@/utils/file-storage-url';
1616

1717
const GalleryBlock = memo(
1818
forwardRef<HTMLDivElement, EditorElementProps<GalleryBlockNode>>(({ node, children, ...attributes }, ref) => {
@@ -32,12 +32,9 @@ const GalleryBlock = memo(
3232
const photos = useMemo(() => {
3333
return images
3434
.map((image) => {
35-
let imageUrl = image.url;
35+
const imageUrl = resolveFileUrl(image.url, workspaceId, viewId);
3636

3737
if (!imageUrl) return null;
38-
if (!isFileURL(image.url)) {
39-
imageUrl = getFileUrl(workspaceId, viewId, image.url);
40-
}
4138

4239
const url = new URL(imageUrl);
4340

src/components/editor/components/blocks/image/ImageRender.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import ImageResizer from '@/components/editor/components/blocks/image/ImageResiz
99
import ImageToolbar from '@/components/editor/components/blocks/image/ImageToolbar';
1010
import Img from '@/components/editor/components/blocks/image/Img';
1111
import { ImageBlockNode } from '@/components/editor/editor.type';
12+
import { Log } from '@/utils/log';
1213

1314

1415
const MIN_WIDTH = 100;
@@ -30,11 +31,13 @@ function ImageRender({
3031

3132
const { width: imageWidth } = useMemo(() => node.data || {}, [node.data]);
3233
const url = node.data.url || localUrl;
34+
35+
Log.debug('[ImageRender] url', { url, localUrl, node: node.data });
3336
const [initialWidth, setInitialWidth] = useState<number | null>(null);
3437
const [newWidth, setNewWidth] = useState<number | null>(imageWidth ?? null);
3538

3639
useEffect(() => {
37-
if(rendered && initialWidth === null && imgRef.current) {
40+
if (rendered && initialWidth === null && imgRef.current) {
3841
setInitialWidth(imgRef.current.offsetWidth);
3942
}
4043
}, [initialWidth, rendered]);
@@ -55,7 +58,7 @@ function ImageRender({
5558
[debounceSubmitWidth],
5659
);
5760

58-
if(!url) return null;
61+
if (!url) return null;
5962

6063
return (
6164
<div
Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { getFileUrl, isFileURL } from '@/utils/file-storage-url';
1+
import { resolveFileUrl } from '@/utils/file-storage-url';
22

33
/**
44
* Constructs the appropriate URL for file/image blocks
@@ -23,20 +23,5 @@ export function constructFileUrl(
2323
return '';
2424
}
2525

26-
// Case 1: Already a full URL (http/https)
27-
// This is the format returned by uploadFile() function
28-
if (isFileURL(dataUrl)) {
29-
return dataUrl;
30-
}
31-
32-
const fileId = dataUrl;
33-
34-
if (viewId) {
35-
return getFileUrl(workspaceId, viewId, fileId);
36-
}
37-
38-
// Fallback without viewId - this will likely fail to load
39-
console.error('File URL construction: viewId not available, file may not load correctly', { fileId });
40-
// Use empty string as viewId fallback, though this may not work
41-
return getFileUrl(workspaceId, '', fileId);
26+
return resolveFileUrl(dataUrl, workspaceId, viewId || '');
4227
}

0 commit comments

Comments
 (0)