Skip to content

Commit a99d855

Browse files
wewoormumiao
authored andcommitted
feat(notification): add simple Notification module
support the basic notification display panel, add, and remove methods re #18
1 parent 53fc63e commit a99d855

15 files changed

+384
-67
lines changed

src/controller/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { EditorController } from './editor';
44
import { ExplorerController } from './explorer/explorer';
55
import { FolderTreeController } from './explorer/folderTree';
66
import { MenuBarController } from './menuBar';
7+
import { NotificationController } from './notification';
78
import { PanelController } from './panel';
89
import { SettingsController } from './settings';
910
import { SidebarController } from './sidebar';
@@ -20,3 +21,4 @@ export const statusBarController = container.resolve(StatusBarController);
2021
export const settingsController = container.resolve(SettingsController);
2122
export const folderTreeController = container.resolve(FolderTreeController);
2223
export const workbenchController = container.resolve(WorkbenchController);
24+
export const notificationController = container.resolve(NotificationController);

src/controller/notification.ts

-30
This file was deleted.

src/controller/notification.tsx

+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import * as React from 'react';
2+
import * as ReactDOM from 'react-dom';
3+
import { connect, IStatusBarItem } from 'mo';
4+
import { Controller } from 'mo/react/controller';
5+
import { notificationService, statusBarService } from 'mo/services';
6+
import { singleton } from 'tsyringe';
7+
import { Notification } from 'mo/workbench/statusBar/notification';
8+
import { NotificationPanel } from 'mo/workbench/statusBar/notification/notificationPanel';
9+
10+
import { IActionBarItem } from 'mo/components/actionBar';
11+
import {
12+
INotificationItem,
13+
NOTIFICATION_CLEAR_ALL,
14+
NOTIFICATION_HIDE,
15+
} from 'mo/model/notification';
16+
import { select } from 'mo/common/dom';
17+
import { ID_APP } from 'mo/common/id';
18+
19+
export interface INotificationController {
20+
onCloseNotification(item: INotificationItem): void;
21+
onClick?: (e: React.MouseEvent, item: IStatusBarItem) => void;
22+
onActionBarClick?(
23+
event: React.MouseEvent<Element, MouseEvent>,
24+
item: IActionBarItem<any>
25+
): void;
26+
}
27+
28+
@singleton()
29+
export class NotificationController
30+
extends Controller
31+
implements INotificationController {
32+
constructor() {
33+
super();
34+
this.init();
35+
}
36+
37+
public onCloseNotification(item: INotificationItem<any>): void {
38+
if (typeof item.id === 'number') {
39+
notificationService.removeNotification(item.id);
40+
}
41+
}
42+
43+
private _notificationPanel: HTMLDivElement | undefined = undefined;
44+
45+
private showHideNotifications() {
46+
if (!this._notificationPanel) {
47+
this.renderNotificationPanel();
48+
}
49+
notificationService.showHideNotifications();
50+
}
51+
52+
public onClick = (e: React.MouseEvent, item: IStatusBarItem) => {
53+
this.showHideNotifications();
54+
};
55+
56+
public onActionBarClick = (
57+
event: React.MouseEvent<Element, MouseEvent>,
58+
item: IActionBarItem<any>
59+
) => {
60+
const action = item.id;
61+
if (action === NOTIFICATION_CLEAR_ALL.id) {
62+
notificationService.showHideNotifications();
63+
} else if (action === NOTIFICATION_HIDE.id) {
64+
this.showHideNotifications();
65+
}
66+
};
67+
68+
private init() {
69+
const notificationItem = notificationService.getState();
70+
const NotificationView = connect(notificationService, Notification);
71+
notificationService.setState({
72+
...notificationItem,
73+
render: () => <NotificationView onClick={this.onClick} />,
74+
});
75+
statusBarService.appendRightItem(notificationItem);
76+
}
77+
78+
public renderNotificationPanel() {
79+
const NotificationPanelView = connect(
80+
notificationService,
81+
NotificationPanel
82+
);
83+
const root = select('#' + ID_APP);
84+
const container = document.createElement('div');
85+
root?.appendChild(container);
86+
ReactDOM.render(
87+
<NotificationPanelView
88+
onActionBarClick={this.onActionBarClick}
89+
onCloseNotification={this.onCloseNotification}
90+
/>,
91+
container
92+
);
93+
this._notificationPanel = container;
94+
}
95+
}

src/controller/statusBar.tsx

-14
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ import { IStatusBarItem, StatusBarEvent } from 'mo';
33
import { Controller } from 'mo/react/controller';
44
import { statusBarService } from 'mo/services';
55
import { singleton } from 'tsyringe';
6-
import { Icon } from 'mo/components/icon';
7-
86
export interface IStatusBarController {
97
onClick?: (e: React.MouseEvent, item: IStatusBarItem) => void;
108
}
@@ -15,13 +13,6 @@ const problems: IStatusBarItem = {
1513
name: 'Problems',
1614
};
1715

18-
const notifications: IStatusBarItem = {
19-
id: 'MoNotification',
20-
sortIndex: 1,
21-
name: 'Notification',
22-
render: () => <Icon type="bell" />,
23-
};
24-
2516
export const editorLineColumnItem: IStatusBarItem = {
2617
id: 'EditorCountInfo',
2718
sortIndex: 2,
@@ -42,13 +33,8 @@ export class StatusBarController
4233
this.emit(StatusBarEvent.onClick, e, item);
4334
};
4435

45-
public notify() {
46-
console.log('service:', statusBarService);
47-
}
48-
4936
private initStatusBar() {
5037
statusBarService.appendLeftItem(problems);
51-
statusBarService.appendRightItem(notifications);
5238
statusBarService.appendRightItem(editorLineColumnItem);
5339
}
5440
}

src/model/notification.ts

-1
This file was deleted.

src/model/notification.tsx

+38-10
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,69 @@
1+
import { IActionBarItem } from 'mo/components/actionBar';
12
import { Icon } from 'mo/components/icon';
23
import * as React from 'react';
34
import { injectable } from 'tsyringe';
45
import { IStatusBarItem } from './workbench/statusBar';
56

6-
export type NotificationStatusType = 'message' | 'normal';
7+
export enum NotificationStatus {
8+
Read = 1,
9+
WaitRead = 2,
10+
}
11+
12+
export interface INotificationItem<T = any> {
13+
id?: number;
14+
value: T;
15+
status?: NotificationStatus;
16+
}
717

818
export interface INotification<T = any> extends IStatusBarItem {
9-
data: T[];
10-
status: NotificationStatusType;
19+
data?: INotificationItem<T>[];
20+
showNotifications?: boolean;
21+
actionBar?: IActionBarItem[];
1122
}
1223

24+
export const NOTIFICATION_CLEAR_ALL: IActionBarItem = {
25+
id: 'ClearAll',
26+
title: 'Clear All Notifications',
27+
iconName: 'codicon-clear-all',
28+
};
29+
30+
export const NOTIFICATION_HIDE: IActionBarItem = {
31+
id: 'HideNotifications',
32+
title: 'Hide Notifications',
33+
iconName: 'codicon-chevron-down',
34+
};
35+
1336
@injectable()
1437
export class NotificationModel<T> implements INotification<T> {
15-
1638
static readonly ID = 'MO_NOTIFICATION';
1739
static readonly NAME = 'Notification';
1840

1941
public id: string;
2042
public name: string;
21-
public data: T[];
43+
public data: INotificationItem<T>[];
2244
public sortIndex: number;
2345
public render: () => ReactNode;
24-
public status: NotificationStatusType;
46+
public showNotifications: boolean;
47+
public actionBar: IActionBarItem[];
2548

2649
constructor(
2750
id: string = NotificationModel.ID,
2851
name: string = NotificationModel.NAME,
29-
data: T[] = [],
52+
data: INotificationItem<T>[] = [],
3053
sortIndex: number = 1,
31-
render: () => ReactNode = () => <Icon type="bell" />,
32-
status: NotificationStatusType = 'normal'
54+
showNotifications: boolean = false,
55+
actionBar: IActionBarItem[] = [
56+
NOTIFICATION_CLEAR_ALL,
57+
NOTIFICATION_HIDE,
58+
],
59+
render: () => ReactNode = () => <Icon type="bell" />
3360
) {
3461
this.id = id;
3562
this.name = name;
3663
this.sortIndex = sortIndex;
3764
this.render = render;
38-
this.status = status;
65+
this.showNotifications = showNotifications;
3966
this.data = data;
67+
this.actionBar = actionBar;
4068
}
4169
}

src/services/index.ts

+8
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ import {
2828
PanelService,
2929
} from './workbench';
3030
import { ISettingsService, SettingsService } from './settingsService';
31+
import {
32+
INotificationService,
33+
NotificationService,
34+
} from './notificationService';
3135

3236
/**
3337
* The Services of Workbench
@@ -42,6 +46,9 @@ const menuBarService = container.resolve<IMenuBarService>(MenuBarService);
4246
const editorService = container.resolve<IEditorService>(EditorService);
4347
const statusBarService = container.resolve<IStatusBarService>(StatusBarService);
4448
const panelService = container.resolve<IPanelService>(PanelService);
49+
const notificationService = container.resolve<INotificationService>(
50+
NotificationService
51+
);
4552

4653
/**
4754
* The ColorTheme service,
@@ -72,4 +79,5 @@ export {
7279
extensionService,
7380
colorThemeService,
7481
settingsService,
82+
notificationService,
7583
};

src/services/notificationService.ts

+87-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,87 @@
1-
// TODO notificationService
1+
import {
2+
INotification,
3+
INotificationItem,
4+
NotificationModel,
5+
NotificationStatus,
6+
} from 'mo/model/notification';
7+
import { Component } from 'mo/react';
8+
import { singleton, container } from 'tsyringe';
9+
import { searchById } from './helper';
10+
11+
export interface INotificationService extends Component<INotification> {
12+
addNotification<T>(item: INotificationItem<T>): null | INotificationItem<T>;
13+
removeNotification(id: number): void;
14+
updateNotification<T>(
15+
item: INotificationItem<T>
16+
): null | INotificationItem<T>;
17+
showHideNotifications(): void;
18+
}
19+
20+
@singleton()
21+
export class NotificationService
22+
extends Component<INotification>
23+
implements INotificationService {
24+
protected state: INotification;
25+
26+
constructor() {
27+
super();
28+
this.state = container.resolve(NotificationModel);
29+
}
30+
31+
public showHideNotifications(): void {
32+
this.setState({
33+
...this.state,
34+
showNotifications: !this.state.showNotifications,
35+
});
36+
}
37+
38+
public updateNotification<T>(
39+
item: INotificationItem<T>
40+
): INotificationItem<T> | null {
41+
const { data = [] } = this.state;
42+
if (data.length > -1) {
43+
const index = data.findIndex(searchById(item.id));
44+
if (index > -1) {
45+
const original = data[index];
46+
data[index] = Object.assign(original, item);
47+
this.setState({
48+
...this.state,
49+
data: [...data],
50+
});
51+
return data[index];
52+
}
53+
}
54+
return null;
55+
}
56+
57+
public removeNotification(id: number): void {
58+
const { data = [] } = this.state;
59+
if (data.length > -1) {
60+
const index = data.findIndex(searchById(id));
61+
if (index > -1) {
62+
data.splice(index, 1);
63+
this.setState({
64+
...this.state,
65+
data: [...data],
66+
});
67+
}
68+
}
69+
}
70+
71+
public addNotification<T>(
72+
item: INotificationItem<T>
73+
): null | INotificationItem<T> {
74+
const { data = [] } = this.state;
75+
if (item) {
76+
if (item.id === undefined) item.id = data.length;
77+
item.status = NotificationStatus.WaitRead;
78+
const arr = [...data, item];
79+
this.setState({
80+
...this.state,
81+
data: arr,
82+
});
83+
return item;
84+
}
85+
return null;
86+
}
87+
}

src/style/common.scss

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ $sidebar: prefix('sidebar');
3838
$statusBar: prefix('statusBar');
3939
$workbench: prefix('workbench');
4040
$mainBench: prefix('mainBench');
41+
$notification: prefix('notification');
4142

4243
// The Naming of BEM Element
4344
@function bem-ele($block, $element) {

src/style/mo.scss

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
@import 'mo/workbench/settings/style';
3131
@import 'mo/workbench/sidebar/style';
3232
@import 'mo/workbench/statusBar/style';
33+
@import 'mo/workbench/statusBar/notification/style';
3334
@import 'mo/workbench/sidebar/explore/style';
3435

3536
.#{$prefix} {

0 commit comments

Comments
 (0)