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

Update labels without refresh #165

Merged
merged 15 commits into from
Aug 15, 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { IssueServiceFactory } from './core/services/factories/factory.issue.ser
import { GithubService } from './core/services/github.service';
import { GithubEventService } from './core/services/githubevent.service';
import { IssueService } from './core/services/issue.service';
import { LabelService } from './core/services/label.service';
import { LoggingService } from './core/services/logging.service';
import { PhaseService } from './core/services/phase.service';
import { SessionFixConfirmationComponent } from './core/services/session-fix-confirmation/session-fix-confirmation.component';
Expand Down Expand Up @@ -64,7 +65,17 @@ import { SharedModule } from './shared/shared.module';
{
provide: AuthService,
useFactory: AuthServiceFactory,
deps: [Router, NgZone, GithubService, UserService, IssueService, PhaseService, GithubEventService, Title, LoggingService]
deps: [
Router,
NgZone,
GithubService,
UserService,
IssueService,
LabelService,
PhaseService,
GithubEventService,
Title,
LoggingService]
},
{
provide: IssueService,
Expand Down
21 changes: 11 additions & 10 deletions src/app/core/models/label.model.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,30 @@
/**
* Represents a label and its attributes.
*/
export class Label {
export class Label implements SimpleLabel {
readonly category: string;
readonly name: string;
readonly formattedName: string; // 'category'.'name' (e.g. severity.Low) if a category exists or 'name' if the category does not exist.
color: string;
definition?: string;

constructor(label: { name: string; color: string; definition?: string }) {
const containsDotRegex = /\.\b/g; // contains dot in middle of name
[this.category, this.name] = containsDotRegex.test(label.name) ? label.name.split('.') : [undefined, label.name];
this.formattedName = this.category === undefined || this.category === '' ? this.name : this.category.concat('.', this.name);
this.color = label.color;
this.definition = label.definition;
}

/**
* Returns the name of the label with the format of
* 'category'.'name' (e.g. severity.Low) if a category exists or
* 'name' if the category does not exist.
*/
public getFormattedName(): string {
return this.category === undefined || this.category === '' ? this.name : this.category.concat('.', this.name);
}

public equals(label: Label) {
return this.name === label.name && this.category === label.category;
}
}

/**
* Represents a simplified label with name and color
*/
export type SimpleLabel = {
formattedName: string;
color: string;
};
3 changes: 3 additions & 0 deletions src/app/core/services/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { uuid } from '../../shared/lib/uuid';
import { GithubService } from './github.service';
import { GithubEventService } from './githubevent.service';
import { IssueService } from './issue.service';
import { LabelService } from './label.service';
import { LoggingService } from './logging.service';
import { PhaseService } from './phase.service';
import { UserService } from './user.service';
Expand Down Expand Up @@ -42,6 +43,7 @@ export class AuthService {
private githubService: GithubService,
private userService: UserService,
private issueService: IssueService,
private labelService: LabelService,
private phaseService: PhaseService,
private githubEventService: GithubEventService,
private titleService: Title,
Expand All @@ -67,6 +69,7 @@ export class AuthService {
this.githubService.reset();
this.userService.reset();
this.issueService.reset(true);
this.labelService.reset();
this.phaseService.reset();
this.githubEventService.reset();
this.logger.reset();
Expand Down
15 changes: 14 additions & 1 deletion src/app/core/services/factories/factory.auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { AuthService } from '../auth.service';
import { GithubService } from '../github.service';
import { GithubEventService } from '../githubevent.service';
import { IssueService } from '../issue.service';
import { LabelService } from '../label.service';
import { LoggingService } from '../logging.service';
// import { MockAuthService } from '../mocks/mock.auth.service';
import { PhaseService } from '../phase.service';
Expand All @@ -17,6 +18,7 @@ export function AuthServiceFactory(
githubService: GithubService,
userService: UserService,
issueService: IssueService,
labelService: LabelService,
phaseService: PhaseService,
githubEventService: GithubEventService,
titleService: Title,
Expand All @@ -30,12 +32,23 @@ export function AuthServiceFactory(
// githubService,
// userService,
// issueService,
// labelService,
// phaseService,
// githubEventService,
// titleService,
// logger
// );
// }
console.log(logger);
return new AuthService(router, ngZone, githubService, userService, issueService, phaseService, githubEventService, titleService, logger);
return new AuthService(
router,
ngZone,
githubService,
userService,
issueService,
labelService,
phaseService,
githubEventService,
titleService,
logger);
}
50 changes: 47 additions & 3 deletions src/app/core/services/label.service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { Label } from '../models/label.model';
import { BehaviorSubject, EMPTY, Observable, of, Subscription, timer } from 'rxjs';
import { catchError, exhaustMap, finalize, map } from 'rxjs/operators';
import { Label, SimpleLabel } from '../models/label.model';
import { GithubService } from './github.service';

/* The threshold to decide if color is dark or light.
Expand All @@ -22,17 +22,55 @@ const COLOR_WHITE = 'ffffff'; // Light color for text with dark background
* from the GitHub repository for the WATcher application.
*/
export class LabelService {
static readonly POLL_INTERVAL = 5000; // 5 seconds

labels: Label[];
simpleLabels: SimpleLabel[];

private labelsPollSubscription: Subscription;
private labelsSubject = new BehaviorSubject<SimpleLabel[]>([]);

constructor(private githubService: GithubService) {}

startPollLabels() {
if (this.labelsPollSubscription) {
return;
}
this.labelsPollSubscription = timer(0, LabelService.POLL_INTERVAL)
.pipe(
exhaustMap(() => {
return this.fetchLabels().pipe(
catchError(() => {
return EMPTY;
})
);
})
)
.subscribe(() => {
this.labelsSubject.next(this.simpleLabels);
});
}

stopPollLabels() {
if (this.labelsPollSubscription) {
this.labelsPollSubscription.unsubscribe();
this.labelsPollSubscription = undefined;
}
}

connect(): Observable<SimpleLabel[]> {
return this.labelsSubject.asObservable();
}

/**
* Fetch labels from Github.
*/
public fetchLabels(): Observable<any> {
return this.githubService.fetchAllLabels().pipe(
map((response) => {
this.labels = this.parseLabelData(response);
this.simpleLabels = this.labels;
this.labelsSubject.next(this.simpleLabels);
return response;
})
);
Expand Down Expand Up @@ -92,4 +130,10 @@ export class LabelService {

return styles;
}

reset() {
this.labels = undefined;
this.simpleLabels = undefined;
this.stopPollLabels();
}
}
3 changes: 3 additions & 0 deletions src/app/issues-viewer/issues-viewer.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { GithubUser } from '../core/models/github-user.model';
import { Repo } from '../core/models/repo.model';
import { GithubService } from '../core/services/github.service';
import { IssueService } from '../core/services/issue.service';
import { LabelService } from '../core/services/label.service';
import { MilestoneService } from '../core/services/milestone.service';
import { PhaseService } from '../core/services/phase.service';
import { TABLE_COLUMNS } from '../shared/issue-tables/issue-tables-columns';
Expand Down Expand Up @@ -34,10 +35,12 @@ export class IssuesViewerComponent implements OnInit, AfterViewInit, OnDestroy {
public phaseService: PhaseService,
public githubService: GithubService,
public issueService: IssueService,
public labelService: LabelService,
public milestoneService: MilestoneService
) {
this.repoChangeSubscription = this.phaseService.repoChanged$.subscribe((newRepo) => {
this.issueService.reset(false);
this.labelService.reset();
this.initialize();
});
}
Expand Down
3 changes: 0 additions & 3 deletions src/app/shared/filter-bar/filter-bar.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,6 @@ export class FilterBarComponent implements OnInit, AfterViewInit, OnDestroy {
* Fetch and initialize all information from repository to populate Issue Dashboard.
*/
private initialize() {
// Fetch labels
this.labelFilterBar.load();

// Fetch milestones and update dropdown filter
this.milestoneSubscription = this.milestoneService.fetchMilestones().subscribe(
(response) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@
<button mat-button (click)="removeAllSelection()">Remove all</button>

<div *ngIf="!hasLabels(input.value)" class="no-labels">No Labels Found!</div>

<div class="scroll-container-wrapper">
<div class="scroll-container">
<mat-selection-list [(ngModel)]="selectedLabelNames" (selectionChange)="updateSelection()">
<mat-list-option
#option
*ngFor="let label of allLabels"
*ngFor="let label of this.labels$ | async;"
[value]="label.name"
[selected]="selectedLabelNames.includes(label.name)"
class="list-option"
Expand All @@ -39,7 +40,7 @@
[disabled]="hiddenLabelNames.has(label.name)"
(click)="simulateClick(option); $event.stopPropagation()"
>
{{ label.name }}
{{ label.formattedName }}
</mat-chip>
</div>
</mat-list-option>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,22 @@
import { Component, Input, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { AfterViewInit, Component, Input, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { MatListOption, MatSelectionList } from '@angular/material/list';
import { BehaviorSubject, Subscription } from 'rxjs';
import { BehaviorSubject, Observable, Subscription } from 'rxjs';
import { SimpleLabel } from '../../../core/models/label.model';
import { LabelService } from '../../../core/services/label.service';
import { LoggingService } from '../../../core/services/logging.service';

export type simplifiedLabel = {
name: string;
color: string;
};

@Component({
selector: 'app-label-filter-bar',
templateUrl: './label-filter-bar.component.html',
styleUrls: ['./label-filter-bar.component.css']
})
export class LabelFilterBarComponent implements OnInit, OnDestroy {
export class LabelFilterBarComponent implements OnInit, AfterViewInit, OnDestroy {
@Input() selectedLabels: BehaviorSubject<string[]>;
@Input() hiddenLabels: BehaviorSubject<Set<string>>;
@ViewChild(MatSelectionList) matSelectionList;

allLabels: simplifiedLabel[];
labels$: Observable<SimpleLabel[]>;
allLabels: SimpleLabel[];
selectedLabelNames: string[] = [];
hiddenLabelNames: Set<string> = new Set();
loaded = false;
Expand All @@ -30,7 +27,16 @@ export class LabelFilterBarComponent implements OnInit, OnDestroy {

ngOnInit() {
this.loaded = false;
this.load();
}

ngAfterViewInit(): void {
setTimeout(() => {
this.load();
this.labels$ = this.labelService.connect();
this.labels$.subscribe((labels) => {
this.allLabels = labels;
});
});
}

ngOnDestroy(): void {
Expand Down Expand Up @@ -70,27 +76,18 @@ export class LabelFilterBarComponent implements OnInit, OnDestroy {

/** loads in the labels in the repository */
public load() {
this.labelService.startPollLabels();
this.labelSubscription = this.labelService.fetchLabels().subscribe(
(response) => {
this.logger.debug('LabelFilterBarComponent: Fetched labels from Github');
},
(err) => {},
() => {
this.initialize();
this.loaded = true;
}
);
}

private initialize() {
this.allLabels = this.labelService.labels.map((label) => {
return {
name: label.getFormattedName(),
color: label.color
};
});
this.loaded = true;
}

filter(filter: string, target: string): boolean {
return !target.toLowerCase().includes(filter.toLowerCase());
}
Expand All @@ -99,7 +96,7 @@ export class LabelFilterBarComponent implements OnInit, OnDestroy {
if (this.allLabels === undefined || this.allLabels.length === 0) {
return false;
}
return this.allLabels.some((label) => !this.filter(filter, label.name));
return this.allLabels.some((label) => !this.filter(filter, label.formattedName));
}

updateSelection(): void {
Expand Down
10 changes: 10 additions & 0 deletions src/app/shared/layout/header.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { ErrorHandlingService } from '../../core/services/error-handling.service
import { GithubService } from '../../core/services/github.service';
import { GithubEventService } from '../../core/services/githubevent.service';
import { IssueService } from '../../core/services/issue.service';
import { LabelService } from '../../core/services/label.service';
import { LoggingService } from '../../core/services/logging.service';
import { PhaseDescription, PhaseService } from '../../core/services/phase.service';
import { UserService } from '../../core/services/user.service';
Expand Down Expand Up @@ -46,6 +47,7 @@ export class HeaderComponent implements OnInit {
private location: Location,
private githubEventService: GithubEventService,
private issueService: IssueService,
private labelService: LabelService,
private errorHandlingService: ErrorHandlingService,
private githubService: GithubService,
private dialogService: DialogService
Expand Down Expand Up @@ -85,6 +87,7 @@ export class HeaderComponent implements OnInit {
// Remove current phase issues and load selected phase issues.
this.githubService.reset();
this.issueService.reset(false);
this.labelService.reset();
this.reload();

// Route app to new phase.
Expand Down Expand Up @@ -155,6 +158,13 @@ export class HeaderComponent implements OnInit {
}
);

this.labelService.fetchLabels().subscribe(
(success) => success,
(error) => {
this.errorHandlingService.handleError(error, () => this.labelService.fetchLabels());
}
);

// Prevent user from spamming the reload button
setTimeout(() => {
this.isReloadButtonDisabled = false;
Expand Down
Loading