Skip to content

Commit

Permalink
Add Stand alone Conversation Page
Browse files Browse the repository at this point in the history
  • Loading branch information
chidozieononiwu committed Sep 17, 2024
1 parent 358bbfb commit 6cdfbfb
Show file tree
Hide file tree
Showing 18 changed files with 200 additions and 70 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import { MessageService } from 'primeng/api';
import { SignalRService } from 'src/app/_services/signal-r/signal-r.service';
import { Subject } from 'rxjs';
import { CommentThreadUpdateAction, CommentUpdatesDto } from 'src/app/_dtos/commentThreadUpdateDto';
import { computeStyles } from '@popperjs/core';

@Component({
selector: 'app-code-panel',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<app-review-page-layout
[review]="review"
[sideMenu]="sideMenu">
<div class="conversation-panel border rounded p-3">
<div class="conversation-panel overflow-auto border rounded py-3">
<app-conversations
[apiRevisions]="apiRevisions"
[comments]="comments"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Component } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { ActivatedRoute, Router } from '@angular/router';
import { MenuItem } from 'primeng/api';
import { Subject, takeUntil } from 'rxjs';
import { REVIEW_ID_ROUTE_PARAM } from 'src/app/_helpers/common-helpers';
Expand Down Expand Up @@ -30,11 +30,15 @@ export class ConversationPageComponent {
private destroy$ = new Subject<void>();

constructor(private route: ActivatedRoute, private reviewsService: ReviewsService, private userProfileService: UserProfileService,
private apiRevisionsService: RevisionsService, private commentsService: CommentsService
private apiRevisionsService: RevisionsService, private commentsService: CommentsService, private router: Router
) {}

ngOnInit() {
this.userProfileService.getUserProfile().subscribe();
this.userProfileService.getUserProfile().subscribe(
(userProfile : any) => {
this.userProfile = userProfile;
}
);
this.reviewId = this.route.snapshot.paramMap.get(REVIEW_ID_ROUTE_PARAM);
this.createSideMenu();
this.loadReview(this.reviewId!);
Expand All @@ -47,12 +51,12 @@ export class ConversationPageComponent {
{
icon: 'bi bi-braces',
tooltip: 'API',
//command: () => { this.conversationSidePanel = !this.conversationSidePanel; }
command: () => this.openLatestAPIReivisonForReview()
},
{
icon: 'bi bi-clock-history',
tooltip: 'Revisions',
//command: () => { this.revisionSidePanel = !this.revisionSidePanel; }
command: () => this.router.navigate([`/revision/${this.reviewId}`])
}
];
}
Expand All @@ -73,7 +77,7 @@ export class ConversationPageComponent {
next: (response: any) => {
this.apiRevisions = response.result;
}
});
});
}

loadComments() {
Expand All @@ -82,6 +86,11 @@ export class ConversationPageComponent {
next: (comments: CommentItemModel[]) => {
this.comments = comments;
}
});
});
}

openLatestAPIReivisonForReview() {
const apiRevision = this.apiRevisions.find(x => x.apiRevisionType === "Automatic") ?? this.apiRevisions[0];
this.apiRevisionsService.openAPIRevisionPage(apiRevision, this.router.url);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
<p *ngIf="commentThreads.size === 0">This Review has no comments</p>
<p-timeline [value]="getAPIRevisionWithComments()">
<div *ngIf="isLoading" class="spinner-border m-3" role="status">
<span class="visually-hidden">Loading...</span>
</div>
<p *ngIf="!isLoading && (getAPIRevisionWithComments()).length == 0" class="ms-4">This Review has no comments</p>
<p-timeline *ngIf="!isLoading" [value]="getAPIRevisionWithComments()">
<ng-template pTemplate="marker" let-apiRevision>
<i class="bi bi-clock-history"></i>
</ng-template>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ export class ConversationsComponent implements OnChanges {
commentThreads: Map<string, CodePanelRowData[]> = new Map<string, CodePanelRowData[]>();
numberOfActiveThreads: number = 0;

apiRevisionsLoaded = false;
commentsLoaded = false;
isLoading: boolean = true;

destroy$ = new Subject<void>();

constructor(private commentsService: CommentsService, private signalRService: SignalRService) { }
Expand All @@ -37,62 +41,76 @@ export class ConversationsComponent implements OnChanges {
}

ngOnChanges(changes: SimpleChanges) {
if (changes['apiRevisions'] || changes['comments']) {
if (this.apiRevisions.length > 0 && this.comments.length > 0) {
this.createCommentThreads();
}
if (changes['apiRevisions']) {
this.apiRevisionsLoaded = true;
}

if (changes['comments']) {
this.commentsLoaded = true;
}

if (this.apiRevisionsLoaded && this.commentsLoaded) {
this.createCommentThreads();
}
}

createCommentThreads() {
this.commentThreads = new Map<string, CodePanelRowData[]>();
this.numberOfActiveThreads = 0;
const apiRevisionInOrder = this.apiRevisions.sort((a, b) => (new Date(b.createdOn) as any) - (new Date(a.createdOn) as any));
const groupedComments = this.comments
.reduce((acc: { [key: string]: CommentItemModel[] }, comment) => {
const key = comment.elementId;
if (!acc[key]) {
acc[key] = [];
}
acc[key].push(comment);
return acc;
}, {});
if (this.apiRevisions.length > 0 && this.comments.length > 0) {
this.commentThreads = new Map<string, CodePanelRowData[]>();
this.numberOfActiveThreads = 0;
const apiRevisionInOrder = this.apiRevisions.sort((a, b) => (new Date(b.createdOn) as any) - (new Date(a.createdOn) as any));
const groupedComments = this.comments
.reduce((acc: { [key: string]: CommentItemModel[] }, comment) => {
const key = comment.elementId;
if (!acc[key]) {
acc[key] = [];
}
acc[key].push(comment);
return acc;
}, {});

for (const elementId in groupedComments) {
if (groupedComments.hasOwnProperty(elementId)) {
const comments = groupedComments[elementId];
const apiRevisionIds = comments.map(c => c.apiRevisionId);
for (const elementId in groupedComments) {
if (groupedComments.hasOwnProperty(elementId)) {
const comments = groupedComments[elementId];
const apiRevisionIds = comments.map(c => c.apiRevisionId);

let apiRevisionPostion = Number.MAX_SAFE_INTEGER;
let apiRevisionPostion = Number.MAX_SAFE_INTEGER;

for (const apiRevisionId of apiRevisionIds) {
const apiRevisionIdPosition = apiRevisionInOrder.findIndex(apiRevision => apiRevision.id === apiRevisionId);
if (apiRevisionIdPosition >= 0 && apiRevisionIdPosition < apiRevisionPostion) {
apiRevisionPostion = apiRevisionIdPosition;
for (const apiRevisionId of apiRevisionIds) {
const apiRevisionIdPosition = apiRevisionInOrder.findIndex(apiRevision => apiRevision.id === apiRevisionId);
if (apiRevisionIdPosition >= 0 && apiRevisionIdPosition < apiRevisionPostion) {
apiRevisionPostion = apiRevisionIdPosition;
}
}
}

if (apiRevisionPostion >= 0 && apiRevisionPostion < apiRevisionInOrder.length) {
const apiRevisionIdForThread = apiRevisionInOrder[apiRevisionPostion].id;
const codePanelRowData = new CodePanelRowData();
codePanelRowData.type = CodePanelRowDatatype.CommentThread;
codePanelRowData.comments = comments;
codePanelRowData.isResolvedCommentThread = comments.some(c => c.isResolved);
if (apiRevisionPostion >= 0 && apiRevisionPostion < apiRevisionInOrder.length) {
const apiRevisionIdForThread = apiRevisionInOrder[apiRevisionPostion].id;
const codePanelRowData = new CodePanelRowData();
codePanelRowData.type = CodePanelRowDatatype.CommentThread;
codePanelRowData.comments = comments;
codePanelRowData.isResolvedCommentThread = comments.some(c => c.isResolved);

if (!codePanelRowData.isResolvedCommentThread) {
this.numberOfActiveThreads++;
}
if (!codePanelRowData.isResolvedCommentThread) {
this.numberOfActiveThreads++;
}

if (this.commentThreads.has(apiRevisionIdForThread)) {
this.commentThreads.get(apiRevisionIdForThread)?.push(codePanelRowData);
}
else {
this.commentThreads.set(apiRevisionIdForThread, [codePanelRowData]);
if (this.commentThreads.has(apiRevisionIdForThread)) {
this.commentThreads.get(apiRevisionIdForThread)?.push(codePanelRowData);
}
else {
this.commentThreads.set(apiRevisionIdForThread, [codePanelRowData]);
}
}
}
}
this.numberOfActiveThreadsEmitter.emit(this.numberOfActiveThreads);
this.isLoading = false;
}
else if (this.apiRevisions.length > 0 && this.comments.length === 0) {
setTimeout(() => {
this.isLoading = false;
}, 1000);
}
this.numberOfActiveThreadsEmitter.emit(this.numberOfActiveThreads);
}

getAPIRevisionWithComments() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,19 +64,24 @@
</ng-template>
</p-splitter>
</app-review-page-layout>
<p-sidebar [(visible)]="revisionSidePanel!" position="right" [modal]="true" styleClass="revisions-sidebar">
<p-sidebar [(visible)]="revisionSidePanel!" position="right" [modal]="true" [showCloseIcon]="false" styleClass="revisions-sidebar">
<app-revisions-list
[review]="review"
[revisionSidePanel]="revisionSidePanel!"></app-revisions-list>
[revisionSidePanel]="revisionSidePanel!">
</app-revisions-list>
</p-sidebar>
<p-sidebar [(visible)]="conversationSidePanel!" position="right" [modal]="true" styleClass="conversation-sidebar">
<h4>Conversations</h4>
<p-divider />
<p-sidebar [(visible)]="conversationSidePanel!" position="right" [modal]="true" [showCloseIcon]="false" styleClass="conversation-sidebar">
<ng-template pTemplate="header">
<div class="flex align-items-center gap-2">
<a [routerLink]="['/conversation', this.reviewId]" class="h4">Conversations</a>
</div>
</ng-template>
<app-conversations
[apiRevisions]="apiRevisions"
[comments]="comments"
[review]="review"
[activeApiRevisionId]="activeApiRevisionId"
(scrollToNodeEmitter)="handleScrollToNodeEmitter($event)"
(numberOfActiveThreadsEmitter)="handleNumberOfActiveThreadsEmitter($event)"></app-conversations >
(numberOfActiveThreadsEmitter)="handleNumberOfActiveThreadsEmitter($event)">
</app-conversations >
</p-sidebar>
Original file line number Diff line number Diff line change
Expand Up @@ -496,8 +496,8 @@ export class ReviewPageComponent implements OnInit {
break;
}
}
}

}
ngOnDestroy() {
this.workerService.terminateWorker();
this.destroy$.next();
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,10 @@

<app-review-page-layout
[review]="review"
[sideMenu]="sideMenu">
<div class="revisions-panel overflow-auto border rounded">
<app-revisions-list
[review]="review"
(apiRevisionsEmitter)="handleApiRevisionsEmitter($event)">
</app-revisions-list>
</div>
</app-review-page-layout>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
:host ::ng-deep {
.revision-panel{
height: calc(100vh - 130px);
background-color: var(--base-fg-color);
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,64 @@
import { Component } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { MenuItem } from 'primeng/api';
import { Subject, takeUntil } from 'rxjs';
import { REVIEW_ID_ROUTE_PARAM } from 'src/app/_helpers/common-helpers';
import { Review } from 'src/app/_models/review';
import { APIRevision } from 'src/app/_models/revision';
import { ReviewsService } from 'src/app/_services/reviews/reviews.service';
import { RevisionsService } from 'src/app/_services/revisions/revisions.service';

@Component({
selector: 'app-revision-page',
templateUrl: './revision-page.component.html',
styleUrls: ['./revision-page.component.scss']
})
export class RevisionPageComponent {
reviewId : string | null = null;
review : Review | undefined = undefined;
sideMenu: MenuItem[] | undefined;
apiRevisions: APIRevision[] = [];

private destroy$ = new Subject<void>();

constructor(private route: ActivatedRoute, private reviewsService: ReviewsService, private apiRevisionsService: RevisionsService, private router: Router) {}

ngOnInit() {
this.reviewId = this.route.snapshot.paramMap.get(REVIEW_ID_ROUTE_PARAM);
this.createSideMenu();
this.loadReview(this.reviewId!);
}

createSideMenu() {
this.sideMenu = [
{
icon: 'bi bi-braces',
tooltip: 'API',
command: () => this.openLatestAPIReivisonForReview()
},
{
icon: 'bi bi-chat-left-dots',
tooltip: 'Conversations',
command: () => this.router.navigate([`/conversation/${this.reviewId}`])
}
];
}

loadReview(reviewId: string) {
this.reviewsService.getReview(reviewId)
.pipe(takeUntil(this.destroy$)).subscribe({
next: (review: Review) => {
this.review = review;
}
});
}

openLatestAPIReivisonForReview() {
const apiRevision = this.apiRevisions.find(x => x.apiRevisionType === "Automatic") ?? this.apiRevisions[0];
this.apiRevisionsService.openAPIRevisionPage(apiRevision, this.router.url);
}

handleApiRevisionsEmitter(apiRevisions: APIRevision[]) {
this.apiRevisions = apiRevisions as APIRevision[];
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, Input, OnChanges, OnInit, SimpleChanges, ViewChild } from '@angular/core';
import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges, ViewChild } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { MenuItem, MessageService, SortEvent } from 'primeng/api';
Expand All @@ -23,6 +23,8 @@ export class RevisionsListComponent implements OnInit, OnChanges {
@Input() review : Review | undefined = undefined;
@Input() revisionSidePanel : boolean = false;

@Output() apiRevisionsEmitter : EventEmitter<APIRevision[]> = new EventEmitter<APIRevision[]>();

@ViewChild("revisionCreationFileUpload") revisionCreationFileUpload!: FileUpload;

assetsPath : string = environment.assetsPath;
Expand Down Expand Up @@ -152,7 +154,7 @@ export class RevisionsListComponent implements OnInit, OnChanges {
this.pagination = response.pagination;
this.totalNumberOfRevisions = this.pagination?.totalCount!;
}

this.apiRevisionsEmitter.emit(this.revisions);
this.setCreateRevisionLanguageBasedOnReview();
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { ActivatedRoute } from "@angular/router";
import { SCROLL_TO_NODE_QUERY_PARAM } from "./common-helpers";

export const REVIEW_PAGE_ROUTE_REGEX = "\/[a-z]+?\/[a-zA-Z0-9]+";

export function getQueryParams(route: ActivatedRoute, excludedKeys: string[] = [SCROLL_TO_NODE_QUERY_PARAM]) {
return route.snapshot.queryParamMap.keys.reduce((params: { [key: string]: any; }, key) => {
if (!excludedKeys.includes(key)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { ConversationPageComponent } from 'src/app/_components/conversation-page/conversation-page.component';
import { ReviewPageLayoutModule } from './shared/review-page-layout.module';
import { SharedAppModule } from './shared/shared-app.module';
import { CommonModule } from '@angular/common';

const routes: Routes = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import { UiScrollModule } from 'ngx-ui-scroll' ;
import { PageOptionsSectionComponent } from 'src/app/_components/shared/page-options-section/page-options-section.component';
import { ReviewPageOptionsComponent } from 'src/app/_components/review-page-options/review-page-options.component';
import { InputSwitchModule } from 'primeng/inputswitch';
import { SharedAppModule } from './shared/shared-app.module';
import { ReviewPageLayoutModule } from './shared/review-page-layout.module';

const routes: Routes = [
Expand Down
Loading

0 comments on commit 6cdfbfb

Please sign in to comment.