Skip to content

Commit 6bbcc75

Browse files
feat(codecatalyst): Dev Env project quickpick #3243
Problem: When using the Create Dev Env webview we used a drop down menu to provide all possible Projects. The problem is that querying all Projects takes time and nothing would show on the screen for a while. Additionally, if the user had many projects from multiple spaces it was not easy to find Solution: - Instead of a dropdown menu use an async quick pick which will continuously load new Projects as they come in the the paginated query for all Projects. - Along with using a quick pick, the user can easily filter the Projects using the quick picks built in filtering mechanism. - Upon initial load of the webview, the fetching of projects is started immediately. This will improve UX. Signed-off-by: Nikolas Komonen <[email protected]>
1 parent 88a283a commit 6bbcc75

File tree

3 files changed

+88
-15
lines changed

3 files changed

+88
-15
lines changed

src/codecatalyst/vue/create/backend.ts

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ import { isCloud9 } from '../../../shared/extensionUtilities'
3232
import { telemetry } from '../../../shared/telemetry/telemetry'
3333
import { isNonNullable } from '../../../shared/utilities/tsUtils'
3434
import { recordSource } from '../../utils'
35+
import { QuickPickPrompter } from '../../../shared/ui/pickerPrompter'
36+
import { createProjectPrompter } from '../../wizards/selectResource'
3537

3638
interface LinkedResponse {
3739
readonly type: 'linked'
@@ -48,6 +50,8 @@ interface EmptyResponse {
4850
export type SourceResponse = LinkedResponse | EmptyResponse
4951

5052
export class CodeCatalystCreateWebview extends VueWebview {
53+
private projectPrompter?: QuickPickPrompter<CodeCatalystProject>
54+
5155
public readonly id = 'createCodeCatalyst'
5256
public readonly source = 'src/codecatalyst/vue/create/index.js'
5357

@@ -57,15 +61,43 @@ export class CodeCatalystCreateWebview extends VueWebview {
5761
private readonly onComplete: (devenv?: DevEnvironment) => void
5862
) {
5963
super()
64+
65+
// When webview first loads, an instance of this class
66+
// is created.
67+
// We build the prompter here since it immeditely starts
68+
// fetching the Projects upon creation.
69+
// When a user triggers the prompt to select a Project the **first** time,
70+
// the fetching of Projects will already be in progress.
71+
this.projectPrompter = createProjectPrompter(this.client)
6072
}
6173

6274
public close() {
6375
this.dispose()
6476
this.onComplete()
6577
}
6678

67-
public async getProjects() {
68-
return this.client.listResources('project').flatten().promise()
79+
/**
80+
* Opens a quick pick that lists all Projects from all Spaces.
81+
*
82+
* @param spaceName Only show Projects from this Space in the quick pick. If undefined,
83+
* shows Projects from all Spaces.
84+
* @returns Project if it was selected, otherwise undefined due to user cancellation.
85+
*/
86+
public async quickPickProject(spaceName?: CodeCatalystOrg['name']): Promise<CodeCatalystProject | undefined> {
87+
// We use an existing prompter since it would have already started
88+
// fetching Projects (improved UX).
89+
if (this.projectPrompter === undefined) {
90+
this.projectPrompter = createProjectPrompter(this.client, spaceName)
91+
}
92+
93+
const selectedProject = await this.projectPrompter.prompt()
94+
this.projectPrompter = undefined
95+
96+
if (!isValidResponse(selectedProject)) {
97+
return
98+
}
99+
100+
return selectedProject
69101
}
70102

71103
public async getBranches(project: CodeCatalystProject) {

src/codecatalyst/vue/create/source.vue

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,23 @@
1212
</div>
1313

1414
<div class="source-pickers" v-if="model.type === 'linked'">
15-
<span style="width: 100%">
16-
<label class="option-label soft">Project</label>
17-
<select class="picker" v-model="model.selectedProject" @input="update">
18-
<option disabled :value="undefined">{{ loadingProjects ? 'Loading...' : 'Select a project' }}</option>
19-
<option v-for="project in projects" :key="project.name" :value="project">
20-
{{ `${project.org.name} / ${project.name}` }}
21-
</option>
22-
</select>
23-
</span>
15+
<div class="modes flex-sizing mt-16">
16+
<span class="flex-sizing mt-8">
17+
<label class="option-label soft">Space</label>
18+
<button class="project-button" @click="quickPickProject()">
19+
{{ selectedSpaceName }}
20+
<span class="icon icon-lg icon-vscode-edit edit-icon"></span>
21+
</button>
22+
</span>
23+
24+
<span class="flex-sizing mt-8">
25+
<label class="option-label soft">Project</label>
26+
<button class="project-button" @click="quickPickProject(model.selectedProject?.org.name)">
27+
{{ selectedProjectName }}
28+
<span class="icon icon-lg icon-vscode-edit edit-icon"></span>
29+
</button>
30+
</span>
31+
</div>
2432

2533
<div class="modes flex-sizing mt-16">
2634
<!-- Existing branch -->
@@ -73,7 +81,7 @@
7381

7482
<script lang="ts">
7583
import { defineComponent } from 'vue'
76-
import { CodeCatalystBranch, CodeCatalystProject } from '../../../shared/clients/codecatalystClient'
84+
import { CodeCatalystBranch, CodeCatalystOrg, CodeCatalystProject } from '../../../shared/clients/codecatalystClient'
7785
import { WebviewClientFactory } from '../../../webviews/client'
7886
import { createClass, createType } from '../../../webviews/util'
7987
import { CodeCatalystCreateWebview, SourceResponse } from './backend'
@@ -112,7 +120,6 @@ export default defineComponent({
112120
},
113121
async created() {
114122
this.loadingProjects = true
115-
this.projects = await client.getProjects().finally(() => (this.loadingProjects = false))
116123
},
117124
watch: {
118125
async 'model.selectedProject'(project?: CodeCatalystProject) {
@@ -162,6 +169,21 @@ export default defineComponent({
162169
return 'Branch already exists'
163170
}
164171
},
172+
isProjectSelected() {
173+
return !!this.model.selectedProject
174+
},
175+
selectedSpaceName() {
176+
if (this.model.selectedProject === undefined) {
177+
return 'Not Selected'
178+
}
179+
return this.model.selectedProject.org.name
180+
},
181+
selectedProjectName() {
182+
if (this.model.selectedProject === undefined) {
183+
return 'Not Selected'
184+
}
185+
return this.model.selectedProject.name
186+
},
165187
},
166188
methods: {
167189
update() {
@@ -181,6 +203,13 @@ export default defineComponent({
181203
})
182204
this.update()
183205
},
206+
async quickPickProject(spaceToGetProjects: CodeCatalystOrg['name'] | undefined = undefined) {
207+
const res = await client.quickPickProject(spaceToGetProjects)
208+
if (res === undefined) {
209+
return
210+
}
211+
this.$emit('update:modelValue', { ...this.modelValue, selectedProject: res })
212+
},
184213
},
185214
emits: {
186215
'update:modelValue': (value: InstanceType<typeof VueModel>) => true,
@@ -242,4 +271,16 @@ body.vscode-light .mode-container[data-disabled='true'] .config-item {
242271
#branch-input {
243272
min-width: 300px;
244273
}
274+
275+
.project-button {
276+
background-color: transparent;
277+
padding-left: 0;
278+
padding-right: 0;
279+
font-weight: bold;
280+
}
281+
282+
.edit-icon {
283+
margin-left: 10px;
284+
color: #0078d7;
285+
}
245286
</style>

src/codecatalyst/wizards/selectResource.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,10 +111,10 @@ export function createOrgPrompter(
111111

112112
export function createProjectPrompter(
113113
client: codecatalyst.CodeCatalystClient,
114-
org?: codecatalyst.CodeCatalystOrg
114+
spaceName?: codecatalyst.CodeCatalystOrg['name']
115115
): QuickPickPrompter<codecatalyst.CodeCatalystProject> {
116116
const helpUri = isCloud9() ? docs.cloud9.main : docs.vscode.main
117-
const projects = org ? client.listProjects({ spaceName: org.name }) : client.listResources('project')
117+
const projects = spaceName ? client.listProjects({ spaceName }) : client.listResources('project')
118118

119119
return createResourcePrompter(projects, helpUri, {
120120
title: 'Select a CodeCatalyst Project',

0 commit comments

Comments
 (0)