Skip to content

Commit eac0cdd

Browse files
committed
fix: improve loading state indications, small refactors
1 parent d630f93 commit eac0cdd

File tree

4 files changed

+96
-86
lines changed

4 files changed

+96
-86
lines changed

src/constants.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
export const BASE_API_URL = 'https://reactnative.directory/api/libraries';
2+
export const KEYWORD_REGEX = /:\w+/g;
3+
4+
export enum ENTRY_OPTION {
5+
INSTALL = 'Install package in the current workspace',
6+
INSTALL_SPECIFIC_VERSION = 'Install specific package version in the current workspace',
7+
VISIT_HOMEPAGE = 'Visit homepage',
8+
VISIT_REPO = 'Visit GitHub repository',
9+
VISIT_NPM = 'Visit npm registry entry',
10+
VIEW_BUNDLEPHOBIA = 'View BundlePhobia analysis',
11+
VIEW_LICENSE = 'View license details',
12+
VIEW_DEPENDENCIES = 'View dependencies',
13+
COPY_NAME = 'Copy package name',
14+
COPY_REPO_URL = 'Copy GitHub repository URL',
15+
COPY_NPM_URL = 'Copy npm registry URL',
16+
GO_BACK = '$(newline) Go back to search',
17+
PLATFORMS = 'Platforms',
18+
COMPATIBILITY = 'Compatibility',
19+
CONFIG_PLUGIN = 'Config plugin',
20+
DIRECTORY_SCORE = 'Directory score'
21+
}
22+
23+
export enum VERSIONS_OPTION {
24+
CANCEL = '$(newline) Cancel'
25+
}
26+
27+
export enum STRINGS {
28+
DEFAULT_TITLE = 'Search in React Native Directory',
29+
PACKAGES_PLACEHOLDER_BUSY = 'Loading directory data...',
30+
PACKAGES_PLACEHOLDER = 'Search for a package'
31+
}
32+
33+
/**
34+
* A subset of API query params mapped with normalized (lowercased) keyword values.
35+
* @see https://github.com/react-native-community/directory/blob/main/types/index.ts#L14
36+
*/
37+
export const VALID_KEYWORDS_MAP = {
38+
android: 'android',
39+
expogo: 'expoGo',
40+
ios: 'ios',
41+
macos: 'macos',
42+
fireos: 'fireos',
43+
horizon: 'horizon',
44+
tvos: 'tvos',
45+
visionos: 'visionos',
46+
vegaos: 'vegaos',
47+
web: 'web',
48+
windows: 'windows',
49+
hasexample: 'hasExample',
50+
hasimage: 'hasImage',
51+
hastypes: 'hasTypes',
52+
ismaintained: 'isMaintained',
53+
ispopular: 'isPopular',
54+
wasrecentlyupdated: 'wasRecentlyUpdated',
55+
newarchitecture: 'newArchitecture',
56+
configplugin: 'configPlugin'
57+
};

src/extension.ts

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,19 @@
11
import { exec } from 'node:child_process';
22
import { commands, env, type ExtensionContext, QuickPickItemKind, Uri, window, workspace } from 'vscode';
33

4+
import { ENTRY_OPTION, KEYWORD_REGEX, STRINGS, VALID_KEYWORDS_MAP, VERSIONS_OPTION } from './constants';
45
import { detectPackageManager } from './detectPackageManager';
5-
import { DirectoryEntry, NpmRegistryData } from './types';
6+
import { DirectoryEntry, NpmRegistryData, ValidKeyword } from './types';
67
import {
78
deduplicateSearchTokens,
8-
ENTRY_OPTION,
99
fetchData,
1010
getCommandToRun,
1111
getCompatibilityList,
1212
getEntryTypeLabel,
1313
getPlatformsList,
1414
invertObject,
15-
KEYWORD_REGEX,
1615
numberFormatter,
17-
openListWithSearch,
18-
STRINGS,
19-
VALID_KEYWORDS_MAP,
20-
ValidKeyword,
21-
VERSIONS_OPTION
16+
openListWithSearch
2217
} from './utils';
2318

2419
export async function activate(context: ExtensionContext) {
@@ -33,17 +28,11 @@ export async function activate(context: ExtensionContext) {
3328
const disposable = commands.registerCommand('extension.showQuickPick', async () => {
3429
const packagesPick = window.createQuickPick<DirectoryEntry>();
3530

36-
packagesPick.placeholder = STRINGS.PLACEHOLDER_BUSY;
37-
packagesPick.title = STRINGS.DEFAULT_TITLE;
31+
packagesPick.placeholder = STRINGS.PACKAGES_PLACEHOLDER_BUSY;
3832
packagesPick.matchOnDescription = false;
3933
packagesPick.matchOnDetail = false;
40-
packagesPick.busy = true;
4134

42-
packagesPick.show();
43-
packagesPick.items = await fetchData();
44-
45-
packagesPick.busy = false;
46-
packagesPick.placeholder = STRINGS.PLACEHOLDER;
35+
await openListWithSearch(packagesPick);
4736

4837
packagesPick.onDidChangeValue(async (value) => {
4938
packagesPick.busy = true;
@@ -165,23 +154,28 @@ export async function activate(context: ExtensionContext) {
165154

166155
switch (selectedAction.label) {
167156
case ENTRY_OPTION.INSTALL: {
157+
optionPick.busy = true;
158+
168159
exec(getCommandToRun(selectedEntry, preferredManager), { cwd: workspacePath }, (error, stout) => {
169160
if (error) {
170161
window.showErrorMessage(
171-
`An error occurred while trying to install the \`${selectedEntry.npmPkg}\` package: ${error.message}`
162+
`An error occurred while trying to install the '${selectedEntry.npmPkg}' package: ${error.message}`
172163
);
164+
optionPick.busy = false;
173165
return;
174166
}
175167
window.showInformationMessage(
176-
`\`${selectedEntry.npmPkg}\` package has been installed${selectedEntry.dev ? ' as `devDependency`' : ''} in current workspace using \`${preferredManager}\`: ${stout}`
168+
`'${selectedEntry.npmPkg}' package has been installed${selectedEntry.dev ? ' as `devDependency`' : ''} in current workspace using '${preferredManager}': ${stout}`
177169
);
178170
optionPick.hide();
179171
});
172+
180173
break;
181174
}
182175
case ENTRY_OPTION.INSTALL_SPECIFIC_VERSION: {
183176
const versionPick = window.createQuickPick();
184177
versionPick.title = `Select "${selectedEntry.label}" package version to install`;
178+
versionPick.busy = true;
185179
versionPick.placeholder = 'Loading versions...';
186180
versionPick.show();
187181

@@ -190,7 +184,11 @@ export async function activate(context: ExtensionContext) {
190184

191185
if (!response.ok) {
192186
window.showErrorMessage(`Cannot fetch package versions from npm registry`);
187+
versionPick.hide();
188+
setupAndShowEntryPicker();
189+
return;
193190
}
191+
194192
const data = (await response.json()) as NpmRegistryData;
195193
const tags = invertObject(data['dist-tags']);
196194

@@ -201,6 +199,7 @@ export async function activate(context: ExtensionContext) {
201199
alwaysShow: true
202200
}));
203201

202+
versionPick.busy = false;
204203
versionPick.placeholder = 'Select a version';
205204
versionPick.items = [
206205
...versions.reverse(),
@@ -217,27 +216,29 @@ export async function activate(context: ExtensionContext) {
217216
return;
218217
}
219218

219+
versionPick.busy = true;
220+
220221
exec(
221222
getCommandToRun(selectedEntry, preferredManager, selectedVersion.label),
222223
{ cwd: workspacePath },
223224
(error, stout) => {
224225
if (error) {
225226
window.showErrorMessage(
226-
`An error occurred while trying to install the \`${selectedEntry.npmPkg}@${selectedVersion.label}\` package: ${error.message}`
227+
`An error occurred while trying to install the '${selectedEntry.npmPkg}@${selectedVersion.label}' package: ${error.message}`
227228
);
228229
versionPick.hide();
229230
setupAndShowEntryPicker();
230231
return;
231232
}
232233
window.showInformationMessage(
233-
`\`${selectedEntry.npmPkg}@${selectedVersion.label}\` package has been installed${selectedEntry.dev ? ' as `devDependency`' : ''} in current workspace using \`${preferredManager}\`: ${stout}`
234+
`'${selectedEntry.npmPkg}@${selectedVersion.label}' package has been installed${selectedEntry.dev ? ' as `devDependency`' : ''} in current workspace using '${preferredManager}': ${stout}`
234235
);
235236
versionPick.hide();
236237
}
237238
);
238239
});
239240
} else {
240-
window.showErrorMessage(`Incompatible response from npm registry`);
241+
window.showErrorMessage(`Recieved incorrect response from npm registry`);
241242
versionPick.hide();
242243
setupAndShowEntryPicker();
243244
}
@@ -314,7 +315,9 @@ export async function activate(context: ExtensionContext) {
314315
}
315316
}
316317

317-
optionPick.hide();
318+
if (selectedAction.label !== ENTRY_OPTION.INSTALL) {
319+
optionPick.hide();
320+
}
318321
});
319322
});
320323
});

src/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import { type QuickPickItem } from 'vscode';
2+
import { VALID_KEYWORDS_MAP } from './constants';
23

34
export type DirectoryEntry = QuickPickItem & PackageData;
45

6+
export type ValidKeyword = keyof typeof VALID_KEYWORDS_MAP;
7+
58
/**
69
* Mirror of React Native Directory library type.
710
* @see https://github.com/react-native-community/directory/blob/main/types/index.ts#L41

src/utils.ts

Lines changed: 11 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,10 @@
11
import { QuickPick, window } from 'vscode';
22

3-
import { DirectoryEntry, PackageData } from './types';
4-
5-
export const BASE_API_URL = 'https://reactnative.directory/api/libraries';
6-
export const KEYWORD_REGEX = /:\w+/g;
3+
import { DirectoryEntry, PackageData, ValidKeyword } from './types';
4+
import { BASE_API_URL, STRINGS } from './constants';
75

86
export const numberFormatter = new Intl.NumberFormat('en-EN', { notation: 'compact' });
97

10-
export enum ENTRY_OPTION {
11-
INSTALL = 'Install package in the current workspace',
12-
INSTALL_SPECIFIC_VERSION = 'Install specific package version in the current workspace',
13-
VISIT_HOMEPAGE = 'Visit homepage',
14-
VISIT_REPO = 'Visit GitHub repository',
15-
VISIT_NPM = 'Visit npm registry entry',
16-
VIEW_BUNDLEPHOBIA = 'View BundlePhobia analysis',
17-
VIEW_LICENSE = 'View license details',
18-
VIEW_DEPENDENCIES = 'View dependencies',
19-
COPY_NAME = 'Copy package name',
20-
COPY_REPO_URL = 'Copy GitHub repository URL',
21-
COPY_NPM_URL = 'Copy npm registry URL',
22-
GO_BACK = '$(newline) Go back to search',
23-
PLATFORMS = 'Platforms',
24-
COMPATIBILITY = 'Compatibility',
25-
CONFIG_PLUGIN = 'Config plugin',
26-
DIRECTORY_SCORE = 'Directory score'
27-
}
28-
29-
export enum VERSIONS_OPTION {
30-
CANCEL = '$(newline) Cancel'
31-
}
32-
33-
export enum STRINGS {
34-
DEFAULT_TITLE = 'Search in React Native Directory',
35-
PLACEHOLDER_BUSY = 'Loading directory data...',
36-
PLACEHOLDER = 'Search for a package'
37-
}
38-
39-
/**
40-
* A subset of API query params mapped with normalized (lowercased) keyword values.
41-
* @see https://github.com/react-native-community/directory/blob/main/types/index.ts#L14
42-
*/
43-
export const VALID_KEYWORDS_MAP = {
44-
android: 'android',
45-
expogo: 'expoGo',
46-
ios: 'ios',
47-
macos: 'macos',
48-
fireos: 'fireos',
49-
horizon: 'horizon',
50-
tvos: 'tvos',
51-
visionos: 'visionos',
52-
vegaos: 'vegaos',
53-
web: 'web',
54-
windows: 'windows',
55-
hasexample: 'hasExample',
56-
hasimage: 'hasImage',
57-
hastypes: 'hasTypes',
58-
ismaintained: 'isMaintained',
59-
ispopular: 'isPopular',
60-
wasrecentlyupdated: 'wasRecentlyUpdated',
61-
newarchitecture: 'newArchitecture',
62-
configplugin: 'configPlugin'
63-
};
64-
export type ValidKeyword = keyof typeof VALID_KEYWORDS_MAP;
65-
668
function getDetailLabel(item: PackageData) {
679
const platforms = getPlatformsList(item);
6810
return [
@@ -167,16 +109,21 @@ export function deduplicateSearchTokens(query: string, tokens: string[]) {
167109
return Array.from(new Set([...query.split(' '), ...formatAsSearchParams(tokens)])).join(' ');
168110
}
169111

170-
export async function openListWithSearch(packagesPick: QuickPick<DirectoryEntry>, query: string = packagesPick.value) {
171-
packagesPick.placeholder = STRINGS.PLACEHOLDER_BUSY;
112+
export async function openListWithSearch(
113+
packagesPick: QuickPick<DirectoryEntry>,
114+
query: string | undefined = packagesPick.value
115+
) {
116+
packagesPick.placeholder = STRINGS.PACKAGES_PLACEHOLDER_BUSY;
172117
packagesPick.busy = true;
173118

174-
packagesPick.value = query;
119+
if (query) {
120+
packagesPick.value = query;
121+
}
175122

176123
packagesPick.show();
177124
packagesPick.items = await fetchData(query);
178125

179-
packagesPick.placeholder = STRINGS.PLACEHOLDER;
126+
packagesPick.placeholder = STRINGS.PACKAGES_PLACEHOLDER;
180127
packagesPick.busy = false;
181128
}
182129

0 commit comments

Comments
 (0)