Skip to content

Commit 42e688b

Browse files
authored
feat: optimize extension overview (#125)
* feat: optimize extension overview * feat: support nls
1 parent ae590ff commit 42e688b

5 files changed

Lines changed: 80 additions & 62 deletions

File tree

packages/extension-manager/src/browser/extension-overview/index.tsx

Lines changed: 48 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,77 @@
11
import React from 'react';
22
import { ReactEditorComponent } from '@opensumi/ide-editor/lib/browser';
33
import { Icon, getKaitianIcon, Button, Tabs } from '@opensumi/ide-components';
4-
import { localize } from '@opensumi/ide-core-common';
4+
import { localize, replaceLocalizePlaceholder } from '@opensumi/ide-core-common';
55
import { Markdown } from '@opensumi/ide-markdown';
6+
import { ProgressBar } from '@opensumi/ide-core-browser/lib/components/progressbar';
7+
import { useInjectable } from '@opensumi/ide-core-browser/lib/react-hooks/injectable-hooks';
68

79
import { VSXExtensionRaw } from '../../common/vsx-registry-types';
810
import * as styles from './overview.module.less';
9-
import { InstallState, VSXExtension } from '../../common';
10-
import { ProgressBar } from '@opensumi/ide-core-browser/lib/components/progressbar';
11+
import { InstallState, IVSXExtensionService, VSXExtension, VSXExtensionServiceToken } from '../../common';
1112

1213
enum TabActiveKey {
1314
details = 'Details',
1415
changelog = 'ChangeLog',
1516
deps = 'Dependencies',
1617
}
1718

18-
const tabMap = [
19-
TabActiveKey.details,
20-
TabActiveKey.changelog,
21-
TabActiveKey.deps,
22-
];
19+
const tabMap = [TabActiveKey.details, TabActiveKey.changelog, TabActiveKey.deps];
2320

2421
interface IExtensionMetadata {
2522
readme?: string;
2623
changelog?: string;
24+
icon?: string;
25+
downloadCount?: number;
2726
installed?: boolean;
2827
}
2928

30-
export const ExtensionOverview: ReactEditorComponent<VSXExtensionRaw & VSXExtension & { state: string }> = ({ resource }) => {
31-
const files = React.useMemo(() => resource.metadata?.files, [resource]);
29+
export const ExtensionOverview: ReactEditorComponent<
30+
VSXExtensionRaw & VSXExtension & { state: string; extensionId: string }
31+
> = ({ resource }) => {
32+
const vsxExtensionService = useInjectable<IVSXExtensionService>(VSXExtensionServiceToken);
3233
const [loading, setLoading] = React.useState(true);
3334
const [activateKey, setActivateKey] = React.useState(TabActiveKey.details);
3435
const [metadata, setMetadata] = React.useState<IExtensionMetadata>({});
3536

36-
const tabs: TabActiveKey[] = React.useMemo(() => {
37-
const res: TabActiveKey[] = [];
38-
if (resource.metadata?.files.readme) {
39-
res.push(TabActiveKey.details);
40-
}
41-
if (resource.metadata?.files.changelog) {
42-
res.push(TabActiveKey.changelog);
43-
}
44-
return res;
45-
}, [resource]);
46-
4737
const onDidTabChange = React.useCallback((index: number) => {
4838
const activeKey = tabMap[index];
4939
if (activeKey) {
5040
setActivateKey(activeKey);
5141
}
5242
}, []);
5343

44+
const getExtensonMetadata = React.useCallback(
45+
({ readme, changelog }: { [prop: string]: string | undefined }) =>
46+
[
47+
readme && fetch(readme).then((res) => res.text()),
48+
changelog && fetch(changelog).then((res) => res.text()),
49+
].filter(Boolean),
50+
[],
51+
);
52+
5453
const initExtensionMetadata = React.useCallback(async () => {
55-
const tasks = [
56-
files?.readme && fetch(files.readme).then((res) => res.text()),
57-
files?.changelog && fetch(files.changelog).then((res) => res.text()),
58-
];
59-
const [readme, changelog] = await Promise.all(tasks);
60-
setMetadata({ readme, changelog });
54+
const extension = await vsxExtensionService.getRemoteRawExtension(resource.metadata!.extensionId);
55+
if (extension) {
56+
const tasks = getExtensonMetadata({ readme: extension.files.readme, changelog: extension.files.changelog });
57+
const [readme, changelog] = await Promise.all(tasks);
58+
setMetadata({ readme, changelog, downloadCount: extension.downloadCount });
59+
}
6160
setLoading(false);
62-
}, [files]);
61+
}, [resource]);
6362

6463
React.useEffect(() => {
6564
initExtensionMetadata();
66-
}, [files]);
65+
}, [resource]);
6766

6867
return (
6968
<div className={styles.extension_overview_container}>
7069
<ProgressBar loading={loading} />
7170
<div className={styles.extension_overview_header}>
72-
<img src={resource.metadata?.files.icon || 'https://open-vsx.org/default-icon.png'} alt={resource.metadata?.displayName} />
71+
<img
72+
src={resource.metadata?.iconUrl || 'https://open-vsx.org/default-icon.png'}
73+
alt={replaceLocalizePlaceholder(resource.metadata?.displayName, resource.metadata?.extensionId)}
74+
/>
7375
<div className={styles.extension_detail}>
7476
<div className={styles.extension_name}>
7577
<h1>
@@ -78,7 +80,8 @@ export const ExtensionOverview: ReactEditorComponent<VSXExtensionRaw & VSXExtens
7880
target='_blank'
7981
rel='noopener noreferrer'
8082
>
81-
{resource.metadata?.displayName || resource.metadata?.name}
83+
{replaceLocalizePlaceholder(resource.metadata?.displayName, resource.metadata?.extensionId) ||
84+
resource.metadata?.name}
8285
</a>
8386
</h1>
8487
<span className={styles.extension_id}>
@@ -93,7 +96,7 @@ export const ExtensionOverview: ReactEditorComponent<VSXExtensionRaw & VSXExtens
9396
</span>
9497
<span>
9598
<Icon iconClass={getKaitianIcon('download')} />
96-
{resource.metadata?.downloadCount}
99+
{resource.metadata?.downloadCount || metadata.downloadCount}
97100
</span>
98101
{resource.metadata?.averageRating && <span>{resource.metadata?.averageRating}</span>}
99102
{resource.metadata?.repository && (
@@ -112,23 +115,27 @@ export const ExtensionOverview: ReactEditorComponent<VSXExtensionRaw & VSXExtens
112115
)}
113116
<span>v{resource.metadata?.version}</span>
114117
</div>
115-
<div className={styles.description}>{resource.metadata?.description}</div>
116-
{resource.metadata?.state === InstallState.NOT_INSTALLED && <div>
117-
<Button size='small' onClick={() => { }}>
118-
{localize('marketplace.extension.install')}
119-
</Button>
120-
</div>}
118+
<div className={styles.description}>
119+
{replaceLocalizePlaceholder(resource.metadata?.description, resource.metadata?.extensionId)}
120+
</div>
121+
{resource.metadata?.state === InstallState.NOT_INSTALLED && (
122+
<div>
123+
<Button size='small' onClick={() => {}}>
124+
{localize('marketplace.extension.install')}
125+
</Button>
126+
</div>
127+
)}
121128
</div>
122129
</div>
123130
<div className={styles.extension_overview_body}>
124131
<Tabs
125132
className={styles.tabs}
126133
value={activateKey}
127134
onChange={onDidTabChange}
128-
tabs={tabs}
135+
tabs={[TabActiveKey.details, TabActiveKey.changelog]}
129136
/>
130-
{activateKey === TabActiveKey.details && metadata.readme && (<Markdown content={metadata.readme} />)}
131-
{activateKey === TabActiveKey.changelog && metadata.changelog && (<Markdown content={metadata.changelog} />)}
137+
{activateKey === TabActiveKey.details && metadata.readme && <Markdown content={metadata.readme} />}
138+
{activateKey === TabActiveKey.changelog && metadata.changelog && <Markdown content={metadata.changelog} />}
132139
</div>
133140
</div>
134141
);

packages/extension-manager/src/browser/extension/index.tsx

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as React from 'react';
22
import { useCallback, useState } from 'react';
3-
import { localize } from '@opensumi/ide-core-common';
3+
import { localize, replaceLocalizePlaceholder } from '@opensumi/ide-core-common';
44
import { Button, Icon, getKaitianIcon } from '@opensumi/ide-components';
55

66
import { InstallState, VSXExtension } from '../../common';
@@ -39,18 +39,25 @@ export const Extension = React.memo(({ extension, onInstall, onClick, installed
3939
<img
4040
className={styles.icon}
4141
src={extension.iconUrl || 'https://open-vsx.org/default-icon.png'}
42-
alt={extension.displayName}
42+
alt={replaceLocalizePlaceholder(extension.displayName, `${extension.publisher}.${extension.name}`)}
4343
/>
4444
<div className={styles.extension_detail}>
4545
<div className={styles.base_info}>
46-
<span className={styles.display_name}>{extension.displayName || extension.name}</span>
46+
<span className={styles.display_name}>
47+
{replaceLocalizePlaceholder(extension.displayName, `${extension.publisher}.${extension.name}`) ||
48+
extension.name}
49+
</span>
4750
<span className={styles.version}>{extension.version}</span>
48-
{!installedState && <span className={styles.download_count}>
49-
<Icon iconClass={getKaitianIcon('download')} />
50-
{extension.downloadCount}
51-
</span>}
51+
{!installedState && (
52+
<span className={styles.download_count}>
53+
<Icon iconClass={getKaitianIcon('download')} />
54+
{extension.downloadCount}
55+
</span>
56+
)}
5257
</div>
53-
<span className={styles.description}>{extension.description}</span>
58+
<span className={styles.description}>
59+
{replaceLocalizePlaceholder(extension.description, `${extension.publisher}.${extension.name}`)}
60+
</span>
5461
<div className={styles.footer}>
5562
<span className={styles.namespace}>{extension.namespace}</span>
5663
{!installedState && (

packages/extension-manager/src/browser/vsx-extension.contribution.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Autowired } from '@opensumi/di';
22
import { ClientAppContribution, ComponentContribution, ComponentRegistry, getIcon } from '@opensumi/ide-core-browser';
3-
import { Domain, localize, URI } from '@opensumi/ide-core-common';
3+
import { Domain, localize, replaceLocalizePlaceholder, URI } from '@opensumi/ide-core-common';
44
import { IMainLayoutService, MainLayoutContribution } from '@opensumi/ide-main-layout';
55
import { BrowserEditorContribution, EditorComponentRegistry, IResource, ResourceService } from '@opensumi/ide-editor/lib/browser';
66
import { IIconService, IconType } from '@opensumi/ide-theme';
@@ -39,20 +39,19 @@ export class VSXExtensionContribution implements ClientAppContribution, MainLayo
3939
registerResource(service: ResourceService) {
4040
service.registerResourceProvider({
4141
scheme: EXTENSION_SCHEME,
42-
provideResource: async (uri: URI): Promise<IResource<Partial<VSXExtensionRaw & { [prop: string]: string }>>> => {
42+
provideResource: async (uri: URI): Promise<IResource<Partial<{ [prop: string]: any }>>> => {
4343
const { extensionId, state } = uri.getParsedQuery();
44-
const extension = await this.vsxExtensionService.getExtension(extensionId);
45-
const iconClass = this.iconService.fromIcon('', extension?.files.icon, IconType.Background);
44+
const extension = await this.vsxExtensionService.getLocalExtension(extensionId);
45+
const iconClass = this.iconService.fromIcon('', extension?.iconUrl, IconType.Background);
4646
return {
4747
uri,
48-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
49-
// @ts-ignore
5048
metadata: {
5149
...extension,
50+
extensionId,
5251
state,
5352
},
5453
icon: iconClass || getIcon('extension'),
55-
name: extension?.displayName || '',
54+
name: replaceLocalizePlaceholder(extension?.displayName, extensionId) || '',
5655
};
5756
},
5857
});

packages/extension-manager/src/browser/vsx-extension.service.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,16 +81,19 @@ export class VSXExtensionService implements IVSXExtensionService {
8181
return extension?.namespace?.toLowerCase() + '.' + extension?.name?.toLowerCase();
8282
}
8383

84-
async getExtension(extensionId: string): Promise<VSXExtensionRaw | undefined> {
84+
async getLocalExtension(extensionId: string): Promise<VSXExtension | undefined> {
85+
const extension = this.extensions.find((e) => this.asExtensionId(e) === extensionId);
86+
87+
return extension || this.installedExtensions.find((e) => this.asExtensionId(e) === extensionId);
88+
}
89+
90+
async getRemoteRawExtension(extensionId: string): Promise<VSXExtensionRaw | undefined> {
8591
const param: QueryParam = {
8692
extensionId,
8793
};
88-
89-
const extension = this.extensions.find((e) => this.asExtensionId(e) === extensionId);
90-
9194
const res = await this.backService.getExtension(param);
9295
if (res && res.extensions && res.extensions.length >= 1) {
93-
return Object.assign(extension || {}, res.extensions[0]);
96+
return Object.assign({}, res.extensions[0]);
9497
}
9598
}
9699

@@ -121,6 +124,7 @@ export class VSXExtensionService implements IVSXExtensionService {
121124
namespace: e.packageJSON.publisher,
122125
name: e.packageJSON.name,
123126
id: e.extensionId,
127+
version: e.packageJSON.version,
124128
displayName: e.packageJSON.displayName,
125129
description: e.packageJSON.description,
126130
publisher: e.packageJSON.publisher,

packages/extension-manager/src/common/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ export const VSXExtensionServiceToken = Symbol('VSXExtensionSerivceToken');
7171
export interface IVSXExtensionService {
7272
search(keyword: string): Promise<void>;
7373
install(extension: VSXExtension): Promise<string | undefined>;
74-
getExtension(extensionId: string): Promise<VSXExtensionRaw | undefined>;
74+
getLocalExtension(extensionId: string): Promise<VSXExtension | undefined>;
75+
getRemoteRawExtension(extensionId: string): Promise<VSXExtensionRaw | undefined>;
7576
openExtensionEditor(extensionId: string, state: InstallState): Promise<void>;
7677

7778
extensions: VSXExtension[];

0 commit comments

Comments
 (0)