diff --git a/src/commands/image/imageSource/buildImageInAzure/SourcePathStep.ts b/src/commands/image/imageSource/buildImageInAzure/SourcePathStep.ts index 22bc2752..34fc01a7 100644 --- a/src/commands/image/imageSource/buildImageInAzure/SourcePathStep.ts +++ b/src/commands/image/imageSource/buildImageInAzure/SourcePathStep.ts @@ -7,20 +7,28 @@ import { AzureWizardPromptStep, type IAzureQuickPickItem } from '@microsoft/vsco import * as path from 'path'; import { browseItem } from '../../../../constants'; import { localize } from '../../../../utils/localize'; +import { quickPickWithBrowse } from '../../../../utils/workspaceUtils'; import { type BuildImageInAzureImageSourceContext } from './BuildImageInAzureImageSourceContext'; export class SourcePathStep extends AzureWizardPromptStep { public async prompt(context: BuildImageInAzureImageSourceContext): Promise { - const srcPath: string | undefined = (await context.ui.showQuickPick(this.getPicks(context), { - placeHolder: localize('sourceDirectoryPick', 'Choose your source code directory'), - suppressPersistence: true - })).data; - - context.srcPath = srcPath ?? (await context.ui.showOpenDialog({ - defaultUri: context.rootFolder?.uri, - canSelectFiles: false, - canSelectFolders: true - }))[0].fsPath; + const srcPath = await quickPickWithBrowse( + context, + this.getPicks(context), + { + placeHolder: localize('sourceDirectoryPick', 'Choose your source code directory'), + suppressPersistence: true, + }, + { + defaultUri: context.rootFolder?.uri, + canSelectFiles: false, + canSelectFolders: true, + }, + ); + + if (srcPath) { + context.srcPath = srcPath; + } } public async configureBeforePrompt(context: BuildImageInAzureImageSourceContext): Promise { diff --git a/src/commands/logStream/logStreamRequest.ts b/src/commands/logStream/logStreamRequest.ts index bbdcc86e..622f8647 100644 --- a/src/commands/logStream/logStreamRequest.ts +++ b/src/commands/logStream/logStreamRequest.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.md in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { AbortController } from '@azure/abort-controller'; import { type ContainerAppsAPIClient } from '@azure/arm-appcontainers'; import { type ServiceClient } from '@azure/core-client'; import { createHttpHeaders, createPipelineRequest } from '@azure/core-rest-pipeline'; diff --git a/src/utils/workspaceUtils.ts b/src/utils/workspaceUtils.ts index 363131b7..0fad4a10 100644 --- a/src/utils/workspaceUtils.ts +++ b/src/utils/workspaceUtils.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { nonNullValue, type IActionContext, type IAzureQuickPickItem } from "@microsoft/vscode-azext-utils"; +import { UserCancelledError, nonNullValue, type IActionContext, type IAzureQuickPickItem, type IAzureQuickPickOptions } from "@microsoft/vscode-azext-utils"; import { basename, relative } from "path"; import { RelativePattern, Uri, workspace, type OpenDialogOptions, type WorkspaceFolder } from "vscode"; import { browseItem, dockerfileGlobPattern, envFileGlobPattern } from "../constants"; @@ -80,11 +80,38 @@ export async function selectWorkspaceFile( }); } - const input: string | undefined = (await context.ui.showQuickPick(quickPicks, { placeHolder })).data; - if (input === skipForNow) { - return undefined; - } else { - return input || (await context.ui.showOpenDialog(options))[0].fsPath; + return await quickPickWithBrowse(context, quickPicks, { placeHolder }, options, skipForNow); +} + +/** + * Shows a quick pick that includes a "Browse..." option. If the user selects "Browse..." and then + * cancels the native file picker dialog, re-prompts the quick pick instead of exiting the wizard. + * + * @param skipValue - If the selected item's data matches this value, returns `undefined` (used for "Skip for now") + */ +export async function quickPickWithBrowse( + context: IActionContext, + picks: IAzureQuickPickItem[], + quickPickOptions: IAzureQuickPickOptions, + openDialogOptions: OpenDialogOptions, + skipValue?: string, +): Promise { + while (true) { + const input: string | undefined = (await context.ui.showQuickPick(picks, quickPickOptions)).data; + if (input === skipValue) { + return undefined; + } else if (input) { + return input; + } else { + try { + return (await context.ui.showOpenDialog(openDialogOptions))[0].fsPath; + } catch (e) { + if (e instanceof UserCancelledError) { + continue; + } + throw e; + } + } } }