Skip to content

Commit 8323ec3

Browse files
authored
feat: triggered rendering image after the image content changed (#4662)
* feat: triggered rendering image after the image content changed * feat: src add tag * feat: code robustness optimization, adding single tests * feat: triggered rendering image after the image content changed * feat: src add tag * feat: code robustness optimization, adding single tests
1 parent c92dcee commit 8323ec3

File tree

4 files changed

+82
-4
lines changed

4 files changed

+82
-4
lines changed

packages/file-scheme/src/browser/preview.view.tsx

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,38 @@
11
import React, { memo } from 'react';
22

3-
import { Disposable, useInjectable } from '@opensumi/ide-core-browser';
3+
import { Disposable, URI, useInjectable } from '@opensumi/ide-core-browser';
44
import { StaticResourceService } from '@opensumi/ide-core-browser/lib/static-resource';
55
import { IResource, ReactEditorComponent } from '@opensumi/ide-editor/lib/browser';
6+
import { IFileServiceClient } from '@opensumi/ide-file-service';
67

78
import styles from './style.module.less';
89

10+
interface UseResourceChangeResult {
11+
fileChanged: number;
12+
}
13+
14+
const useResourceChange = (resource: IResource): UseResourceChangeResult => {
15+
const fileServiceClient = useInjectable<IFileServiceClient>(IFileServiceClient);
16+
const [fileChanged, setFileChanged] = React.useState(0);
17+
18+
React.useEffect(() => {
19+
const disposable = fileServiceClient.onImageFilesChanged((events) => {
20+
const hasResourceChanged = events.some((event) => {
21+
const eventUri = typeof event.uri === 'string' ? new URI(event.uri) : event.uri;
22+
return eventUri.isEqual(resource.uri);
23+
});
24+
25+
if (hasResourceChanged) {
26+
setFileChanged((prev) => prev + 1);
27+
}
28+
});
29+
30+
return () => disposable.dispose();
31+
}, [resource.uri, fileServiceClient]);
32+
33+
return { fileChanged };
34+
};
35+
936
const useResource = (resource: IResource) => {
1037
const staticService = useInjectable<StaticResourceService>(StaticResourceService);
1138

@@ -29,16 +56,24 @@ export const ImagePreview: ReactEditorComponent<null> = (props) => {
2956
const imgRef = React.useRef<HTMLImageElement>();
3057
const imgContainerRef = React.useRef<HTMLDivElement>();
3158
const { src } = useResource(props.resource);
59+
const { fileChanged } = useResourceChange(props.resource);
3260

3361
React.useEffect(() => {
3462
const disposer = new Disposable();
3563
if (imgRef.current) {
36-
imgRef.current.src = src;
64+
try {
65+
const url = new URL(src);
66+
url.searchParams.set('_t', fileChanged.toString());
67+
imgRef.current.src = url.href;
68+
} catch (error) {
69+
// 如果 src 不是有效的 URL,直接使用原始 src
70+
imgRef.current.src = src;
71+
}
3772
}
3873
return () => {
3974
disposer.dispose();
4075
};
41-
}, [props.resource]);
76+
}, [src, fileChanged]);
4277

4378
return (
4479
<div className={styles.kt_image_preview}>

packages/file-service/__tests__/browser/file-service-client.test.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import fs from 'fs-extra';
22
import temp from 'temp';
33

44
import { WSChannelHandler } from '@opensumi/ide-connection/lib/browser';
5-
import { DisposableCollection, FileUri, UTF8 } from '@opensumi/ide-core-common';
5+
import { DisposableCollection, FileChangeType, FileUri, UTF8 } from '@opensumi/ide-core-common';
66
import { createBrowserInjector } from '@opensumi/ide-dev-tool/src/injector-helper';
77
import { FileService } from '@opensumi/ide-file-service/lib/node';
88
import { DiskFileSystemProvider } from '@opensumi/ide-file-service/lib/node/disk-file-system.provider';
@@ -165,6 +165,30 @@ describe('FileServiceClient should be work', () => {
165165
await watcher.dispose();
166166
});
167167

168+
it('onImageFilesChanged event', (done) => {
169+
async function inner() {
170+
fileServiceClient.onImageFilesChanged((event) => {
171+
// 期望只收到一个image的event
172+
expect(event.every((v) => v.uri.includes('a.jpg'))).toBeTruthy();
173+
done();
174+
});
175+
// 触发两个change
176+
await fileServiceClient.fireFilesChange({
177+
changes: [
178+
{
179+
uri: 'a.jpg',
180+
type: FileChangeType.UPDATED,
181+
},
182+
{
183+
uri: 'a.log',
184+
type: FileChangeType.UPDATED,
185+
},
186+
],
187+
});
188+
}
189+
inner();
190+
});
191+
168192
it('set fileExcludes', async () => {
169193
const targetDir = tempDir.resolve('watch-file-exclude-temp-dir');
170194
await fs.ensureDir(targetDir.codeUri.fsPath);

packages/file-service/src/browser/file-service-client.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ import {
5151
TextDocumentContentChangeEvent,
5252
containsExtraFileMethod,
5353
} from '../common';
54+
import { EXT_LIST_IMAGE } from '../common/file-ext';
5455

5556
import { FileSystemWatcher } from './watcher';
5657

@@ -83,6 +84,8 @@ export class FileServiceClient implements IFileServiceClient, IDisposable {
8384

8485
protected readonly _onWatcherFailed = new Emitter<FileWatcherFailureParams>();
8586
readonly onWatcherFailed: Event<FileWatcherFailureParams> = this._onWatcherFailed.event;
87+
protected readonly _onImageFilesChanged = new Emitter<FileChangeEvent>();
88+
readonly onImageFilesChanged: Event<FileChangeEvent> = this._onImageFilesChanged.event;
8689

8790
protected readonly _onFileProviderChanged = new Emitter<string[]>();
8891
readonly onFileProviderChanged: Event<string[]> = this._onFileProviderChanged.event;
@@ -172,6 +175,7 @@ export class FileServiceClient implements IFileServiceClient, IDisposable {
172175
}
173176

174177
public dispose() {
178+
this._onImageFilesChanged.dispose();
175179
return this.toDisposable.dispose();
176180
}
177181

@@ -358,8 +362,21 @@ export class FileServiceClient implements IFileServiceClient, IDisposable {
358362
type: change.type,
359363
} as FileChange),
360364
);
365+
366+
// 触发所有文件变化事件
361367
this._onFilesChanged.fire(changes);
362368
this.eventBus.fire(new FilesChangeEvent(changes));
369+
370+
// 过滤图片文件变化并触发专门的事件
371+
const imageChanges = changes.filter((change) => {
372+
const uri = new URI(change.uri);
373+
const ext = uri.path.ext?.toLowerCase().replace('.', '');
374+
return ext && EXT_LIST_IMAGE.has(ext);
375+
});
376+
377+
if (imageChanges.length > 0) {
378+
this._onImageFilesChanged.fire(imageChanges);
379+
}
363380
}
364381

365382
fireWatcherOverflow(event: FileWatcherOverflowParams): void {

packages/file-service/src/common/file-service-client.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ export interface IFileServiceClient {
3434

3535
onFilesChanged: Event<FileChangeEvent>;
3636

37+
onImageFilesChanged: Event<FileChangeEvent>;
38+
3739
onFileProviderChanged: Event<string[]>;
3840

3941
onWatcherOverflow?: Event<FileWatcherOverflowParams>;

0 commit comments

Comments
 (0)