Skip to content

Commit

Permalink
refactor slack notifications builder
Browse files Browse the repository at this point in the history
  • Loading branch information
Serghei Paduret committed Jul 29, 2021
1 parent 2f1b27d commit ce5c146
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 115 deletions.
13 changes: 2 additions & 11 deletions src/app/components/options/options.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export class OptionsComponent extends DisposableComponent implements OnInit {

this.settings = settingsService.getExtensionSettings();

this.enableBrowserNotifications = !!this.settings.notifications.browser;
this.enableBrowserNotifications = this.settings.notifications.browser;
this.enableSlackNotifications = !!this.settings.notifications.slack;
this.slackSettings = this.settings.notifications.slack || new SlackSettings();
this.bitbucketSettings = this.settings.bitbucket || new BitbucketSettings();
Expand Down Expand Up @@ -163,16 +163,7 @@ export class OptionsComponent extends DisposableComponent implements OnInit {
}

onBrowserNotificationTest() {
this.notificationService
.requestPermission()
.subscribe(permission => {
if (permission === 'granted') {
new Notification('test', {
icon: 'icon64.png',
body: 'hello'
});
}
});
this.notificationService.sendBrowserNotification('test', 'hello', 'https://www.mozilla.org')
}
}

5 changes: 4 additions & 1 deletion src/app/other/notification.titles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export function GetSlackNotificationTitle(options: NotificationOptions): string
return title;
}

export function GetWindowsNotificationBody(action: PullRequestActivityAction) {
export function GetWindowsNotificationBody(action: PullRequestActivityAction): string {
let body;
switch (action) {
case PullRequestActivityAction.Commented:
Expand All @@ -64,6 +64,9 @@ export function GetWindowsNotificationBody(action: PullRequestActivityAction) {
case PullRequestActivityAction.Conflicted:
body = 'pull request got code conflicts';
break;
default:
body = `an action taken over pull request: ${action}`;
break;
}

return body;
Expand Down
111 changes: 111 additions & 0 deletions src/app/other/slack-notification.builder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import {NotificationOptions, PullRequestIssue} from '../models/models';
import {GetSlackNotificationTitle} from './notification.titles';
import {SlackMessageOptions} from '../services/slackClient';
import {PullRequestActivityAction} from '../models/enums';

export class SlackNotificationBuilder {
private readonly message: SlackMessageOptions;

constructor(private memberId: string, private options: NotificationOptions, private issues: PullRequestIssue[]) {
let title = GetSlackNotificationTitle(this.options);

this.message = {
channel: this.memberId,
text: title,
blocks: []
};

// add title with PR link
// todo: use link to the comment for Commented action
this.message.blocks?.push({
'type': 'section',
'text': {
'type': 'mrkdwn',
'text': `${title}: *<${options.pullRequest.links.self[0].href}|${options.pullRequest.title}>*`
}
});
}

private addComment() {
if (this.options.action == PullRequestActivityAction.Commented) {
this.message.blocks?.push({
'type': 'section',
'text': {
'type': 'mrkdwn',
'text': `_${this.options.comment?.text}_`
}
});
}

return this;
}

private addDescription() {
let descriptionMaxLength = 50;
if (this.options.pullRequest.description?.length) {
let prDescription: string;
if (this.options.action === PullRequestActivityAction.Opened) {
// use longer description for just created PR
descriptionMaxLength = 200;
}

// todo: fix description
let spaceIndex = this.options.pullRequest.description.indexOf(' ', descriptionMaxLength);
prDescription = this.options.pullRequest.description.length > descriptionMaxLength && spaceIndex > descriptionMaxLength
? (this.options.pullRequest.description.substr(0, spaceIndex + 1)) + '...'
: this.options.pullRequest.description;

this.message.blocks?.push({
'type': 'section',
'text': {
'type': 'mrkdwn',
'text': `*Description:*\n${prDescription}\n`
}
});
}
return this;
}

private addIssues() {
if (this.issues.length) {
this.message.blocks?.push({
'type': 'section',
'text': {
'type': 'mrkdwn',
'text': `*Issues:* ${this.issues.map(i => `<${i.url}|${i.key}>`).join(', ')}`
}
});
}
return this;
}

private addContext() {
const data = this.options.pullRequest;
this.message.blocks?.push({
'type': 'context',
'elements': [
{
'text':
`*PR created by <${data.author.user.links?.self[0].href}|${data.author.user.displayName}>` +
` at ${new Date(data.createdDate).toDateString()}*` +
` | <${data.fromRef.repository.links?.self[0].href}|${data.fromRef.repository.name}>` +
` \`${data.toRef.displayId}\`` +
`${data.properties.commentCount ? (` :memo:${data.properties.commentCount}`) : ''}`,
'type': 'mrkdwn'
}
]
});
return this;
}

public build(): SlackMessageOptions {
this
.addComment()
// .addDescription()
.addIssues()
.addContext();

this.message.blocks?.push({'type': 'divider'});
return this.message;
}
}
131 changes: 28 additions & 103 deletions src/app/services/notification.service.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import {Injectable} from '@angular/core';
import {DataService} from './data.service';
import {SlackClient, SlackMessageOptions} from './slackClient';
import {SlackClient} from './slackClient';
import {catchError} from 'rxjs/operators';
import {from, Observable, of, throwError} from 'rxjs';
import {NotificationOptions, PullRequestIssue} from '../models/models';
import {NotificationOptions} from '../models/models';
import {BitbucketService} from './bitbucket.service';
import {PullRequestActivityAction} from '../models/enums';
import {GetSlackNotificationTitle, GetWindowsNotificationBody} from '../other/notification.titles';
import {GetWindowsNotificationBody} from '../other/notification.titles';
import {SlackNotificationBuilder} from '../other/slack-notification.builder';

@Injectable()
export class NotificationService {

constructor(private dataService: DataService, private bitbucketService: BitbucketService) {
}

Expand All @@ -22,28 +21,38 @@ export class NotificationService {
return of(Notification.permission);
}

sendNotification(options: NotificationOptions) {
sendBrowserNotification(title: string, body: string, clickUrl?: string) {
this
.requestPermission()
.subscribe((permission: NotificationPermission) => {
if (permission === 'granted') {
let notification = new Notification(title, {
icon: 'icon64.png',
body: body
});

if (clickUrl) {
notification.onclick = (event) => {
// prevent the browser from focusing the Notification's tab
event.preventDefault();
window.open(clickUrl, '_blank');
};
}
}
});
}

sendNotification(options: NotificationOptions) {
// skip notification if it is snoozed for this PR
let snoozeSettings = this.dataService.getNotificationSnoozeSettings();
if (snoozeSettings.includes(options.pullRequest.id)) {
return;
}

let extensionSettings = this.dataService.getExtensionSettings();

if (extensionSettings.notifications.browser) {
this.requestPermission()
.subscribe(permission => {
if (permission === 'granted') {
let body = GetWindowsNotificationBody(options.action);

new Notification(options.pullRequest.title, {
icon: 'favicon.ico',
body: body
});
}
});
let body = GetWindowsNotificationBody(options.action);
this.sendBrowserNotification(options.pullRequest.title, body, options.pullRequest.links.self[0].href);
}

// check if slack is enabled
Expand All @@ -57,7 +66,7 @@ export class NotificationService {
.getPullRequestIssues(pr.fromRef.repository.project.key, pr.fromRef.repository.slug, pr.id)
.pipe(catchError(() => of([])))
.subscribe(issues => {
let messageOptions = NotificationService.buildPullRequestSlackMessage(slackSettings.memberId, options, issues);
let messageOptions = new SlackNotificationBuilder(slackSettings.memberId, options, issues).build();

slackClient
.postMessage(messageOptions)
Expand Down Expand Up @@ -93,88 +102,4 @@ export class NotificationService {
chrome.browserAction.setTitle({title: options.title});
}
}

public static buildPullRequestSlackMessage(memberId: string, options: NotificationOptions, issues: PullRequestIssue[]): SlackMessageOptions {
let title = GetSlackNotificationTitle(options);
let data = options.pullRequest;

let message: SlackMessageOptions = {
channel: memberId,
text: title,
blocks: []
};

// add title with PR link
// todo: use link to the comment for Commented action
message.blocks?.push({
'type': 'section',
'text': {
'type': 'mrkdwn',
'text': `${title}: *<${data.links.self[0].href}|${data.title}>*`
}
});

// add comment
if (options.action == PullRequestActivityAction.Commented) {
message.blocks?.push({
'type': 'section',
'text': {
'type': 'mrkdwn',
'text': `_${options.comment?.text}_`
}
});
}

// add description
let descriptionMaxLength = 50;
if (data.description?.length) {
let prDescription: string;
if (options.action === PullRequestActivityAction.Opened) {
// use longer description for just created PR
descriptionMaxLength = 200;
}
let spaceIndex = data.description.indexOf(' ', descriptionMaxLength);
prDescription = data.description.length > descriptionMaxLength && spaceIndex > descriptionMaxLength
? (data.description.substr(0, spaceIndex + 1)) + '...'
: data.description;

message.blocks?.push({
'type': 'section',
'text': {
'type': 'mrkdwn',
'text': `*Description:*\n${prDescription}\n`
}
});
}

// add issues
if (issues.length) {
message.blocks?.push({
'type': 'section',
'text': {
'type': 'mrkdwn',
'text': `*Issues:* ${issues.map(i => `<${i.url}|${i.key}>`).join(', ')}`
}
});
}

// add context
message.blocks?.push({
'type': 'context',
'elements': [
{
'text':
`*PR created by <${data.author.user.links?.self[0].href}|${data.author.user.displayName}>` +
` at ${new Date(data.createdDate).toDateString()}*` +
` | <${data.fromRef.repository.links?.self[0].href}|${data.fromRef.repository.name}>` +
` \`${data.toRef.displayId}\`` +
`${data.properties.commentCount ? (` :memo:${data.properties.commentCount}`) : ''}`,
'type': 'mrkdwn'
}
]
});

message.blocks?.push({'type': 'divider'});
return message;
}
}

0 comments on commit ce5c146

Please sign in to comment.