From c24cd87e36a803b6cddd66d975610fc31697066d Mon Sep 17 00:00:00 2001 From: bk1012 Date: Mon, 4 Nov 2024 11:21:42 +0800 Subject: [PATCH] feat: support MessageOptions.detail (#4135) * feat: support MessageOptions.detail * feat: change the message open method to an object parameter * chore: optimized code * style: adjust the dialog-content font size --------- Co-authored-by: hacke2 --- packages/components/src/dialog/index.tsx | 10 +++--- packages/components/src/dialog/styles.less | 2 +- .../src/browser/fs-resource/fs-resource.ts | 10 +++--- .../editor/src/browser/untitled-resource.ts | 10 +++--- .../src/browser/workbench-editor.service.ts | 10 +++--- .../api/main.thread.file-system-event.ts | 4 +-- .../browser/vscode/api/main.thread.message.ts | 4 +-- .../src/hosted/api/vscode/ext.host.message.ts | 4 +++ .../browser/dialog/window-dialog.service.tsx | 30 ++++++++-------- .../overlay/src/browser/dialog.service.ts | 23 +++++++----- packages/overlay/src/browser/dialog.view.tsx | 2 ++ .../overlay/src/browser/message.service.tsx | 16 +++++---- packages/overlay/src/common/index.ts | 36 +++++++++---------- packages/search/src/browser/replace.ts | 10 +++--- .../search/src/browser/search.contribution.ts | 13 ++++--- .../types/vscode/typings/vscode.message.d.ts | 6 ++++ .../src/browser/refactor-preview.service.tsx | 18 +++++----- 17 files changed, 116 insertions(+), 92 deletions(-) diff --git a/packages/components/src/dialog/index.tsx b/packages/components/src/dialog/index.tsx index ff6132c039..b3f9643af8 100644 --- a/packages/components/src/dialog/index.tsx +++ b/packages/components/src/dialog/index.tsx @@ -24,6 +24,7 @@ export interface IDialogProps extends IOverlayProps { type?: ModalType; icon?: IconDesc; message: string | React.ReactNode; + detail?: string; buttons?: JSX.Element[] | JSX.Element; closable?: boolean; onOk?: () => void; @@ -46,11 +47,11 @@ const DefaultButtons = ({ onCancel, onOk, cancelText, okText }) => ( export const DialogContent: React.FC = ({ onClose, closable, + type = 'confirm', messageType = MessageType.Info, icon, message, buttons, - type = 'confirm', title, onOk, onCancel, @@ -69,8 +70,8 @@ export const DialogContent: React.FC = ({ /> )}
- {type !== 'basic' && title &&

{title}

} - {message} +

{title}

+ {message && {message}}
{closable && type !== 'basic' && ( @@ -97,6 +98,7 @@ export const Dialog: React.FC = ({ messageType, icon, message, + detail, buttons, type = 'confirm', title, @@ -123,6 +125,6 @@ export const Dialog: React.FC = ({ afterClose={afterClose} {...restProps} > - + ); diff --git a/packages/components/src/dialog/styles.less b/packages/components/src/dialog/styles.less index ffc6634154..d9a7e0ff47 100644 --- a/packages/components/src/dialog/styles.less +++ b/packages/components/src/dialog/styles.less @@ -36,7 +36,7 @@ .@{prefix}-dialog-content_title { line-height: 22px; - font-size: 14px; + font-size: 15px; margin: 0px 0px 8px 0px; } } diff --git a/packages/editor/src/browser/fs-resource/fs-resource.ts b/packages/editor/src/browser/fs-resource/fs-resource.ts index 10105d6c1d..127db781fb 100644 --- a/packages/editor/src/browser/fs-resource/fs-resource.ts +++ b/packages/editor/src/browser/fs-resource/fs-resource.ts @@ -234,11 +234,11 @@ export class FileSystemResourceProvider extends WithEventBus implements IResourc [localize('file.prompt.save', 'Save')]: AskSaveResult.SAVE, [localize('file.prompt.cancel', 'Cancel')]: AskSaveResult.CANCEL, }; - const selection = await this.dialogService.open( - formatLocalize('saveChangesMessage', resource.name), - MessageType.Info, - Object.keys(buttons), - ); + const selection = await this.dialogService.open({ + message: formatLocalize('saveChangesMessage', resource.name), + type: MessageType.Info, + buttons: Object.keys(buttons), + }); const result = buttons[selection!]; return this.close(resource, result); } diff --git a/packages/editor/src/browser/untitled-resource.ts b/packages/editor/src/browser/untitled-resource.ts index 9565b7bb51..5005fa312e 100644 --- a/packages/editor/src/browser/untitled-resource.ts +++ b/packages/editor/src/browser/untitled-resource.ts @@ -211,11 +211,11 @@ export class UntitledSchemeResourceProvider extends WithEventBus implements IRes [localize('file.prompt.save', 'Save')]: AskSaveResult.SAVE, [localize('file.prompt.cancel', 'Cancel')]: AskSaveResult.CANCEL, }; - const selection = await this.dialogService.open( - formatLocalize('saveChangesMessage', resource.name), - MessageType.Info, - Object.keys(buttons), - ); + const selection = await this.dialogService.open({ + message: formatLocalize('saveChangesMessage', resource.name), + type: MessageType.Info, + buttons: Object.keys(buttons), + }); return await this.close(resource, buttons[selection!]); } } diff --git a/packages/editor/src/browser/workbench-editor.service.ts b/packages/editor/src/browser/workbench-editor.service.ts index 8588545ad3..cbde99393a 100644 --- a/packages/editor/src/browser/workbench-editor.service.ts +++ b/packages/editor/src/browser/workbench-editor.service.ts @@ -563,11 +563,11 @@ export class WorkbenchEditorServiceImpl extends WithEventBus implements Workbenc filesDetail += formatLocalize('file.prompt.more.number', toClose.length - MAX_CONFIRM_RESOURCES); } } - const selection = await this.dialogService.open( - toMarkdown(formatLocalize('saveNFilesChangesMessage', toClose.length, filesDetail), this.openner), - MessageType.Info, - Object.keys(buttons), - ); + const selection = await this.dialogService.open({ + message: toMarkdown(formatLocalize('saveNFilesChangesMessage', toClose.length, filesDetail), this.openner), + type: MessageType.Info, + buttons: Object.keys(buttons), + }); const result = buttons[selection!]; if (result === AskSaveResult.SAVE) { await Promise.all(toClose.map((v) => this.resourceService.close?.(v.resource, AskSaveResult.SAVE))); diff --git a/packages/extension/src/browser/vscode/api/main.thread.file-system-event.ts b/packages/extension/src/browser/vscode/api/main.thread.file-system-event.ts index 9989d60157..255f7bba86 100644 --- a/packages/extension/src/browser/vscode/api/main.thread.file-system-event.ts +++ b/packages/extension/src/browser/vscode/api/main.thread.file-system-event.ts @@ -199,7 +199,7 @@ export class MainThreadFileSystemEvent extends Disposable { localize('refactoring-changes.msg.skipChanges'), ]; // edit#metadata.needsConfirmation#true --> show dialog - const answer = await this.dialogService.open(message, MessageType.Info, choices); + const answer = await this.dialogService.open({ message, type: MessageType.Info, buttons: choices }); showPreview = 'show'; if (answer === choices[1]) { // Skip changes @@ -211,7 +211,7 @@ export class MainThreadFileSystemEvent extends Disposable { localize('refactoring-changes.msg.skipChanges'), localize('component.modal.okText'), ]; - const answer = await this.dialogService.open(message, MessageType.Info, choices); + const answer = await this.dialogService.open({ message, type: MessageType.Info, buttons: choices }); if (answer === choices[1]) { // Skip changes return; diff --git a/packages/extension/src/browser/vscode/api/main.thread.message.ts b/packages/extension/src/browser/vscode/api/main.thread.message.ts index e88356fdac..8298179000 100644 --- a/packages/extension/src/browser/vscode/api/main.thread.message.ts +++ b/packages/extension/src/browser/vscode/api/main.thread.message.ts @@ -31,8 +31,8 @@ export class MainThreadMessage implements IMainThreadMessage { from, ): Promise { const action = options.modal - ? await this.dialogService.open(message, type, actions) - : await this.messageService.open(message, type, actions, true, from); + ? await this.dialogService.open({ message, type, buttons: actions, options }) + : await this.messageService.open({ message, type, buttons: actions, closable: true, from }); return action ? actions.indexOf(action) : undefined; } } diff --git a/packages/extension/src/hosted/api/vscode/ext.host.message.ts b/packages/extension/src/hosted/api/vscode/ext.host.message.ts index 751e35569f..03f5cfd005 100644 --- a/packages/extension/src/hosted/api/vscode/ext.host.message.ts +++ b/packages/extension/src/hosted/api/vscode/ext.host.message.ts @@ -43,6 +43,10 @@ export class ExtHostMessage implements IExtHostMessage { } else { if ('modal' in optionsOrFirstItem) { options.modal = optionsOrFirstItem.modal; + + if ('detail' in optionsOrFirstItem) { + options.detail = optionsOrFirstItem.detail; + } } } } diff --git a/packages/file-tree-next/src/browser/dialog/window-dialog.service.tsx b/packages/file-tree-next/src/browser/dialog/window-dialog.service.tsx index cb63a47f49..b94d2598e2 100644 --- a/packages/file-tree-next/src/browser/dialog/window-dialog.service.tsx +++ b/packages/file-tree-next/src/browser/dialog/window-dialog.service.tsx @@ -130,14 +130,13 @@ export class WindowDialogServiceImpl implements IWindowDialogService { } await fileTreeDialogService.whenReady; const model = FileTreeDialogModel.createModel(this.injector, fileTreeDialogService); - const res = await this.dialogService.open( - , - MessageType.Empty, - [], - true, - undefined, - { className: styles.file_dialog_wrapper }, - ); + const res = await this.dialogService.open({ + message: , + type: MessageType.Empty, + buttons: [], + closable: true, + props: { className: styles.file_dialog_wrapper }, + }); this.dialogService.reset(); if (res && res.length > 0) { const files = res.map((r) => URI.file(r)); @@ -180,14 +179,13 @@ export class WindowDialogServiceImpl implements IWindowDialogService { } await fileTreeDialogService.whenReady; const model = FileTreeDialogModel.createModel(this.injector, fileTreeDialogService); - const res = await this.dialogService.open( - , - MessageType.Empty, - [], - true, - undefined, - { className: styles.file_dialog_wrapper }, - ); + const res = await this.dialogService.open({ + message: , + type: MessageType.Empty, + buttons: [], + closable: true, + props: { className: styles.file_dialog_wrapper }, + }); this.dialogService.reset(); if (res && res.length > 0) { const file = URI.file(res[0]); diff --git a/packages/overlay/src/browser/dialog.service.ts b/packages/overlay/src/browser/dialog.service.ts index 6f223267e8..1d66a492f3 100644 --- a/packages/overlay/src/browser/dialog.service.ts +++ b/packages/overlay/src/browser/dialog.service.ts @@ -1,7 +1,7 @@ import { Autowired, Injectable } from '@opensumi/di'; import { Deferred, Emitter, MessageType } from '@opensumi/ide-core-common'; -import { AbstractMessageService, IDialogService, Icon } from '../common'; +import { AbstractMessageService, IDialogService, Icon, OpenMessageOptions } from '../common'; import { DialogContextKey } from './dialog.contextkey'; @@ -18,6 +18,8 @@ export class DialogService extends AbstractMessageService implements IDialogServ protected message: string | React.ReactNode = ''; + protected detail: string | undefined; + protected title = ''; public closable = true; @@ -36,17 +38,18 @@ export class DialogService extends AbstractMessageService implements IDialogServ return this._visible; } - open( - message: string | React.ReactNode, - type: MessageType, - buttons?: any[], + open({ + message, + type, + buttons, + options, closable = true, - _?: string, - props?: Record, - ): Promise { + props, + }: OpenMessageOptions): Promise { this.deferred = new Deferred(); this.type = type; this.message = message; + this.detail = options?.detail; this._visible = true; this.closable = closable; this.props = props ?? {}; @@ -75,6 +78,10 @@ export class DialogService extends AbstractMessageService implements IDialogServ return this.message; } + getDetail(): string | undefined { + return this.detail; + } + getType(): MessageType | undefined { return this.type; } diff --git a/packages/overlay/src/browser/dialog.view.tsx b/packages/overlay/src/browser/dialog.view.tsx index 17d690f690..dc36eda66c 100644 --- a/packages/overlay/src/browser/dialog.view.tsx +++ b/packages/overlay/src/browser/dialog.view.tsx @@ -9,6 +9,7 @@ export const Dialog: FC = () => { const dialogService = useInjectable(IDialogService); const icon = dialogService.getIcon(); const message = dialogService.getMessage(); + const detail = dialogService.getDetail(); const buttons = dialogService.getButtons(); const type = dialogService.getType(); @@ -48,6 +49,7 @@ export const Dialog: FC = () => { closable={dialogService.closable ?? true} afterClose={afterClose} message={message} + detail={detail} type='confirm' messageType={type} icon={icon} diff --git a/packages/overlay/src/browser/message.service.tsx b/packages/overlay/src/browser/message.service.tsx index ccd3878335..8105de67de 100644 --- a/packages/overlay/src/browser/message.service.tsx +++ b/packages/overlay/src/browser/message.service.tsx @@ -6,7 +6,9 @@ import { parseWithoutEscape } from '@opensumi/ide-components/lib/utils'; import { IOpenerService, toMarkdown } from '@opensumi/ide-core-browser'; import { MayCancelablePromise, MessageType, localize, uuid } from '@opensumi/ide-core-common'; -import { AbstractMessageService, IMessageService, MAX_MESSAGE_LENGTH } from '../common'; +import { AbstractMessageService, IMessageService, MAX_MESSAGE_LENGTH, OpenMessageOptions } from '../common'; + +import type vscode from 'vscode'; @Injectable() export class MessageService extends AbstractMessageService implements IMessageService { @@ -37,13 +39,13 @@ export class MessageService extends AbstractMessageService implements IMessageSe * @param closable true | false * @param from from extension */ - open( - rawMessage: string | React.ReactNode, - type: MessageType, - buttons?: string[], + open({ + type, + buttons, + from, closable = true, - from?: string, - ): MayCancelablePromise { + message: rawMessage, + }: OpenMessageOptions): MayCancelablePromise { if (!rawMessage) { return Promise.resolve(undefined); } diff --git a/packages/overlay/src/common/index.ts b/packages/overlay/src/common/index.ts index 114dac132e..c06858091a 100644 --- a/packages/overlay/src/common/index.ts +++ b/packages/overlay/src/common/index.ts @@ -2,8 +2,19 @@ import React from 'react'; import { Event, MayCancelablePromise, MessageType, URI } from '@opensumi/ide-core-common'; +import type vscode from 'vscode'; + export const IMessageService = Symbol('IMessageService'); +export interface OpenMessageOptions { + message: string | React.ReactNode; + type: MessageType; + buttons?: any[]; + options?: vscode.MessageOptions; + closable?: boolean; + from?: string; + props?: Record; +} export interface IMessageService { info( message: string | React.ReactNode, @@ -23,14 +34,7 @@ export interface IMessageService { closable?: boolean, props?: Record, ): MayCancelablePromise; - open( - message: string | React.ReactNode, - type: MessageType, - buttons?: string[], - closable?: boolean, - from?: string, - props?: Record, - ): MayCancelablePromise; + open(options: OpenMessageOptions): MayCancelablePromise; hide(value?: T): void; } @@ -47,6 +51,7 @@ export interface IDialogService extends IMessageService { visible: boolean; onDidDialogVisibleChange: Event; getMessage(): string | React.ReactNode; + getDetail(): string | undefined; getIcon(): Icon | undefined; getButtons(): string[] | undefined; getType(): MessageType | undefined; @@ -61,7 +66,7 @@ export abstract class AbstractMessageService implements IMessageService { closable?: boolean, props?: Record, ): Promise { - return this.open(message, MessageType.Info, buttons, closable, undefined, props); + return this.open({ message, type: MessageType.Info, buttons, closable, props }); } warning( @@ -70,7 +75,7 @@ export abstract class AbstractMessageService implements IMessageService { closable?: boolean, props?: Record, ): Promise { - return this.open(message, MessageType.Warning, buttons, closable, undefined, props); + return this.open({ message, type: MessageType.Warning, buttons, closable, props }); } error( @@ -79,16 +84,9 @@ export abstract class AbstractMessageService implements IMessageService { closable?: boolean, props?: Record, ): Promise { - return this.open(message, MessageType.Error, buttons, closable, undefined, props); + return this.open({ message, type: MessageType.Error, buttons, closable, props }); } - abstract open( - message: string | React.ReactNode, - type: MessageType, - buttons?: any[], - closable?: boolean, - from?: string, - props?: Record, - ): Promise; + abstract open(options: OpenMessageOptions): Promise; abstract hide(value?: T): void; } diff --git a/packages/search/src/browser/replace.ts b/packages/search/src/browser/replace.ts index 57f730db6d..50060c2ec6 100644 --- a/packages/search/src/browser/replace.ts +++ b/packages/search/src/browser/replace.ts @@ -27,16 +27,16 @@ export async function replaceAll( [localize('search.replace.buttonCancel')]: false, [localize('search.replace.buttonOK')]: true, }; - const selection = await dialogService!.open( - formatLocalize( + const selection = await dialogService!.open({ + message: formatLocalize( 'search.removeAll.occurrences.files.confirmation.message', String(resultTotal.resultNum), String(resultTotal.fileNum), replaceText, ), - MessageType.Warning, - Object.keys(buttons), - ); + type: MessageType.Warning, + buttons: Object.keys(buttons), + }); if (!buttons[selection!]) { return buttons[selection!]; } diff --git a/packages/search/src/browser/search.contribution.ts b/packages/search/src/browser/search.contribution.ts index 0a32c296a4..6f4a397d3e 100644 --- a/packages/search/src/browser/search.contribution.ts +++ b/packages/search/src/browser/search.contribution.ts @@ -159,11 +159,14 @@ export class SearchContribution [localize('search.replace.buttonCancel')]: false, [localize('search.replace.buttonOK')]: true, }; - const selection = await this.dialogService.open( - formatLocalize('search.removeAll.occurrences.file.confirmation.message', String(contentSearchResult.length)), - MessageType.Warning, - Object.keys(buttons), - ); + const selection = await this.dialogService.open({ + message: formatLocalize( + 'search.removeAll.occurrences.file.confirmation.message', + String(contentSearchResult.length), + ), + type: MessageType.Warning, + buttons: Object.keys(buttons), + }); if (selection && !buttons[selection]) { return buttons[selection]; } diff --git a/packages/types/vscode/typings/vscode.message.d.ts b/packages/types/vscode/typings/vscode.message.d.ts index 43da733ea2..08ef426b4c 100644 --- a/packages/types/vscode/typings/vscode.message.d.ts +++ b/packages/types/vscode/typings/vscode.message.d.ts @@ -181,5 +181,11 @@ declare module 'vscode' { * Indicates that this message should be modal. */ modal?: boolean; + + /** + * Human-readable detail message that is rendered less prominent. _Note_ that detail + * is only shown for {@link MessageOptions.modal modal} messages. + */ + detail?: string; } } diff --git a/packages/workspace-edit/src/browser/refactor-preview.service.tsx b/packages/workspace-edit/src/browser/refactor-preview.service.tsx index 518ef44726..05eefddfd3 100644 --- a/packages/workspace-edit/src/browser/refactor-preview.service.tsx +++ b/packages/workspace-edit/src/browser/refactor-preview.service.tsx @@ -97,14 +97,16 @@ export class RefactorPreviewServiceImpl implements IRefactorPreviewService { this.registerRefactorPreviewView(); if (this.previewDeferred) { - const continued = await this.dialogService.open( -
- {localize('refactor-preview.overlay.title')} -

{localize('refactor-preview.overlay.detail')}

-
, - MessageType.Warning, - [localize('refactor-preview.overlay.cancel'), localize('refactor-preview.overlay.continue')], - ); + const continued = await this.dialogService.open({ + message: ( +
+ {localize('refactor-preview.overlay.title')} +

{localize('refactor-preview.overlay.detail')}

+
+ ), + type: MessageType.Warning, + buttons: [localize('refactor-preview.overlay.cancel'), localize('refactor-preview.overlay.continue')], + }); if (continued === localize('refactor-preview.overlay.cancel')) { return [];