import {AfterViewInit, Component, Input, ViewChild} from '@angular/core';
import {TimeFormatter} from '@app/issue-detail/time-formatter';
import {IssueTimelineItem} from '../../../generated/graphql-dgql';
import {DataList} from '@app/data-dgql/query';
import DataService from '@app/data-dgql';
import {ListType, NodeId, NodeType} from '@app/data-dgql/id';
import {QueryComponent} from '@app/utils/query-component/query.component';
export interface CoalescedTimelineItem {
user: string;
type: string;
isCoalesced: boolean;
item: Array<IssueTimelineItem> | IssueTimelineItem;
time: string;
}
type ItemFilterFunction = (IssueTimelineItem) => boolean;
@Component({
selector: 'app-timeline',
templateUrl: './timeline.component.html',
styleUrls: ['./timeline.component.scss']
})
export class TimelineComponent implements AfterViewInit {
static readonly COALESCABLE_EVENTS: Map<string, ItemFilterFunction> = new Map([
[
'LabelledEvent',
(item) => {
return !!item.label;
}
],
[
'UnlabelledEvent',
(item) => {
return !!item.removedLabel;
}
],
[
'AddedToComponentEvent',
(item) => {
return !!item.component;
}
],
[
'RemovedFromComponentEvent',
(item) => {
return !!item.removedComponent;
}
],
[
'AddedToLocationEvent',
(item) => {
return !!item.location;
}
],
[
'RemovedFromLocationEvent',
(item) => {
return !!item.removedLocation;
}
],
[
'LinkEvent',
(item) => {
return !!item.linkedIssue;
}
],
[
'UnlinkEvent',
(item) => {
return !!item.removedLinkedIssue;
}
]
]);
public timeFormatter = new TimeFormatter();
timelineItems: Array<CoalescedTimelineItem> = [];
public timelineItems$: DataList<IssueTimelineItem, unknown>;
@Input() issueId: NodeId;
@Input() projectID: string;
@ViewChild(QueryComponent) query: QueryComponent;
private static shouldStopCoalescing(previousItem: IssueTimelineItem, nextItem: IssueTimelineItem): boolean {
return (
previousItem.createdBy.id !== previousItem.createdBy.id ||
Math.abs(Date.parse(nextItem.createdAt) - Date.parse(nextItem.createdAt)) > 60000
);
}
constructor(private dataService: DataService) {}
ngAfterViewInit(): void {
this.requestTimelineItems();
}
requestTimelineItems(): void {
this.timelineItems$ = this.dataService.getList({
node: this.issueId,
type: ListType.TimelineItems
});
this.timelineItems$.count = 99999;
this.query.listenTo(this.timelineItems$, (value) => {
this.prepareTimelineItems(value);
});
}
prepareTimelineItems(items: Map<string, IssueTimelineItem>): void {
let coalescingType: string = null;
let coalesceList = new Array<IssueTimelineItem>();
const coalesced: Array<CoalescedTimelineItem> = [];
for (const timelineItem of items.values()) {
const itemType: string = (timelineItem as any).__typename;
const filter = TimelineComponent.COALESCABLE_EVENTS.get(itemType);
let stopCoalescing = false;
if (coalescingType) {
stopCoalescing = TimelineComponent.shouldStopCoalescing(coalesceList[0], timelineItem);
}
if (coalescingType !== itemType || stopCoalescing) {
this.finishCoalescing(coalesceList, coalesced);
coalesceList = [];
if (filter && filter(timelineItem)) {
coalescingType = itemType;
coalesceList.push(timelineItem);
} else {
coalescingType = null;
this.addSingleCoalesceItem(timelineItem, filter, coalesced);
}
continue;
} else if (coalescingType === null) {
this.addSingleCoalesceItem(timelineItem, filter, coalesced);
continue;
}
if (filter(timelineItem)) {
coalesceList.push(timelineItem);
}
}
this.finishCoalescing(coalesceList, coalesced);
this.timelineItems = coalesced;
}
private userName(item: IssueTimelineItem) {
if (item.createdBy) {
return item.createdBy.displayName;
}
return 'Deleted User';
}
private finishCoalescing(coalesceList: IssueTimelineItem[], coalesced: CoalescedTimelineItem[]): void {
if (coalesceList.length === 0) {
return;
}
const firstItem: any = coalesceList[0];
const itemType = firstItem.__typename;
const createdBy = this.userName(firstItem);
if (coalesceList.length > 1) {
coalesced.push({
type: itemType,
isCoalesced: true,
item: coalesceList,
user: createdBy,
time: coalesceList[0].createdAt
});
} else if (coalesceList.length === 1) {
coalesced.push({
type: itemType,
isCoalesced: false,
item: coalesceList[0],
user: createdBy,
time: coalesceList[0].createdAt
});
}
}
private addSingleCoalesceItem(
timelineItem: IssueTimelineItem,
filter: ItemFilterFunction | undefined,
coalesced: CoalescedTimelineItem[]
): void {
if (!filter || filter(timelineItem)) {
coalesced.push({
type: (timelineItem as any).__typename,
isCoalesced: false,
item: timelineItem,
user: this.userName(timelineItem),
time: timelineItem.createdAt
});
}
}
makeCommentId(node): NodeId {
return {type: NodeType.IssueComment, id: node.id};
}
}