Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Welcome Dialog Service #176580

Merged
merged 10 commits into from Mar 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/vs/base/browser/ui/dialog/dialog.ts
Expand Up @@ -375,6 +375,8 @@ export class Dialog extends Disposable {
this.iconElement.classList.add(...ThemeIcon.asClassNameArray(Codicon.loading), spinModifierClassName);
break;
case 'none':
this.iconElement.classList.add('no-codicon');
break;
case 'info':
case 'question':
default:
Expand Down
38 changes: 36 additions & 2 deletions src/vs/workbench/browser/web.api.ts
Expand Up @@ -314,6 +314,11 @@ export interface IWorkbenchConstructionOptions {
*/
readonly initialColorTheme?: IInitialColorTheme;

/**
* Welcome view dialog on first launch. Can be dismissed by the user.
*/
readonly welcomeDialog?: IWelcomeDialog;

//#endregion


Expand Down Expand Up @@ -510,10 +515,10 @@ export interface IWelcomeBanner {
/**
* Optional actions to appear as links after the welcome banner message.
*/
actions?: IWelcomeBannerAction[];
actions?: IWelcomeLinkAction[];
}

export interface IWelcomeBannerAction {
export interface IWelcomeLinkAction {

/**
* The link to open when clicking. Supports command invocation when
Expand Down Expand Up @@ -578,6 +583,35 @@ export interface IInitialColorTheme {
readonly colors?: { [colorId: string]: string };
}

export interface IWelcomeDialog {

/**
* Unique identifier of the welcome dialog. The identifier will be used to determine
* if the dialog has been previously displayed.
*/
id: string;

/**
* Title of the welcome dialog.
*/
title: string;

/**
* Button text of the welcome dialog.
*/
buttonText: string;

/**
* Message text and icon for the welcome dialog.
*/
messages: { message: string; icon: string }[];

/**
* Optional action to appear as links at the bottom of the welcome dialog.
*/
action?: IWelcomeLinkAction;
}

export interface IDefaultView {

/**
Expand Down
@@ -0,0 +1,54 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-dialog-box {
border-radius: 6px;
}

.monaco-dialog-box .dialog-message-row .dialog-message-container {
padding-left: 0px;
}

.monaco-dialog-box .dialog-message-row .dialog-message-container .dialog-message-text {
font-size: 25px;
width: max-content;
}

.monaco-dialog-box .dialog-message-row .dialog-message-container .dialog-message-body .dialog-message {
display: flex;
padding: 20px;
min-height: auto;
color: var(--vscode-descriptionForeground);
border-radius: 6px;
overflow: hidden;
margin-bottom: 12px;
background: var(--vscode-welcomePage-tileHoverBackground);
align-items: center;
}

.monaco-dialog-box .dialog-message-row .dialog-message-container .dialog-message-body>.dialog-message {
grid-area: title;
align-self: flex-end;
}

.monaco-dialog-box .dialog-message-row .dialog-message-container .dialog-message-body .dialog-message hr {
border-color: var(--vscode-descriptionForeground);
margin-bottom: 12px;
border-width: thin;
}

.monaco-dialog-box .dialog-message-row .dialog-message-container .dialog-message-body .dialog-message .icon-widget {
padding-right: 8px;
max-width: 30px;
max-height: 30px;
position: relative;
top: auto;
color: var(--vscode-textLink-foreground);
padding-right: 20px;
font-size: 25px;
}

.monaco-dialog-box .dialog-message-row .dialog-message-container .dialog-message-body .dialog-message .description-container {
font-size: 16px;
}
@@ -0,0 +1,44 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { Registry } from 'vs/platform/registry/common/platform';
import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService';
import { IWelcomeDialogService as IWelcomeDialogService } from 'vs/workbench/contrib/welcomeDialog/browser/welcomeDialogService';

class WelcomeDialogContribution {

private static readonly WELCOME_DIALOG_DISMISSED_KEY = 'workbench.dialog.welcome.dismissed';

constructor(
@IWelcomeDialogService welcomeDialogService: IWelcomeDialogService,
@IStorageService storageService: IStorageService,
@IBrowserWorkbenchEnvironmentService environmentService: IBrowserWorkbenchEnvironmentService
) {
const welcomeDialog = environmentService.options?.welcomeDialog;
if (!welcomeDialog) {
return;
}

if (storageService.getBoolean(WelcomeDialogContribution.WELCOME_DIALOG_DISMISSED_KEY + '#' + welcomeDialog.id, StorageScope.PROFILE, false)) {
return;
}

welcomeDialogService.show({
title: welcomeDialog.title,
buttonText: welcomeDialog.buttonText,
messages: welcomeDialog.messages,
action: welcomeDialog.action,
onClose: () => {
storageService.store(WelcomeDialogContribution.WELCOME_DIALOG_DISMISSED_KEY + '#' + welcomeDialog.id, true, StorageScope.PROFILE, StorageTarget.USER);
}
});
}
}

Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench)
.registerWorkbenchContribution(WelcomeDialogContribution, LifecyclePhase.Restored);
103 changes: 103 additions & 0 deletions src/vs/workbench/contrib/welcomeDialog/browser/welcomeDialogService.ts
@@ -0,0 +1,103 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import 'vs/css!./media/welcomeDialog';
import { IInstantiationService, createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { ThemeIcon } from 'vs/base/common/themables';
import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { ILayoutService } from 'vs/platform/layout/browser/layoutService';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { Dialog } from 'vs/base/browser/ui/dialog/dialog';
import { defaultButtonStyles, defaultCheckboxStyles, defaultDialogStyles, defaultInputBoxStyles } from 'vs/platform/theme/browser/defaultStyles';
import { $ } from 'vs/base/browser/dom';
import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels';
import { ILinkDescriptor, Link } from 'vs/platform/opener/browser/link';

interface IWelcomeDialogItem {
readonly title: string;
readonly messages: { message: string; icon: string }[];
readonly buttonText: string;
readonly action?: ILinkDescriptor;
readonly onClose?: () => void;
}

export const IWelcomeDialogService = createDecorator<IWelcomeDialogService>('welcomeDialogService');

export interface IWelcomeDialogService {
readonly _serviceBrand: undefined;

show(item: IWelcomeDialogItem): void;
}

export class WelcomeDialogService implements IWelcomeDialogService {
declare readonly _serviceBrand: undefined;

private dialog: Dialog | undefined;
private disposableStore: DisposableStore = new DisposableStore();

constructor(
@ILayoutService private readonly layoutService: ILayoutService,
@IInstantiationService private readonly instantiationService: IInstantiationService,) {
}

private static iconWidgetFor(icon: string) {
const themeIcon = ThemeIcon.fromId(icon);
if (themeIcon) {
const widget = $(ThemeIcon.asCSSSelector(themeIcon));
widget.classList.add('icon-widget');
return widget;
}
return '';
}

async show(welcomeDialogItem: IWelcomeDialogItem): Promise<void> {

this.disposableStore.clear();

const renderBody = (parent: HTMLElement) => {

parent.classList.add(...('dialog-items'));
parent.appendChild(document.createElement('hr'));

for (const message of welcomeDialogItem.messages) {
const descriptorComponent =
$('.dialog-message',
{},
WelcomeDialogService.iconWidgetFor(message.icon),
$('.description-container', {},
$('.description.description.max-lines-3', { 'x-description-for': 'description' }, ...renderLabelWithIcons(message.message))));
parent.appendChild(descriptorComponent);
}

const actionsContainer = $('div.dialog-action-container');
parent.appendChild(actionsContainer);
if (welcomeDialogItem.action) {
this.disposableStore.add(this.instantiationService.createInstance(Link, actionsContainer, welcomeDialogItem.action, {}));
}
};

this.dialog = new Dialog(
this.layoutService.container,
welcomeDialogItem.title,
[welcomeDialogItem.buttonText],
{
detail: '',
type: 'none',
renderBody: renderBody,
disableCloseAction: true,
buttonStyles: defaultButtonStyles,
checkboxStyles: defaultCheckboxStyles,
inputBoxStyles: defaultInputBoxStyles,
dialogStyles: defaultDialogStyles
});

this.disposableStore.add(this.dialog);
await this.dialog.show();
this.disposableStore.dispose();
}
}

registerSingleton(IWelcomeDialogService, WelcomeDialogService, InstantiationType.Eager);

3 changes: 3 additions & 0 deletions src/vs/workbench/workbench.web.main.ts
Expand Up @@ -134,6 +134,9 @@ import 'vs/workbench/contrib/debug/browser/extensionHostDebugService';
// Welcome Banner
import 'vs/workbench/contrib/welcomeBanner/browser/welcomeBanner.contribution';

// Welcome Dialog
import 'vs/workbench/contrib/welcomeDialog/browser/welcomeDialog.contribution';

// Webview
import 'vs/workbench/contrib/webview/browser/webview.web.contribution';

Expand Down