Skip to content

Commit

Permalink
Merge branch 'develop' into 7085/v2/accept-messaging-code-of-conduct
Browse files Browse the repository at this point in the history
  • Loading branch information
nityanandaz authored Oct 13, 2023
2 parents bddf9b2 + 443f4c7 commit 705d9ea
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export class CourseDiscussionComponent extends CourseDiscussionDirective impleme
});
});
this.postsSubscription = this.metisService.posts.pipe().subscribe((posts: Post[]) => {
this.posts = posts;
this.posts = posts.slice();
this.isLoading = false;
});
this.totalItemsSubscription = this.metisService.totalNumberOfPosts.pipe().subscribe((totalItems: number) => {
Expand Down
68 changes: 32 additions & 36 deletions src/main/webapp/app/shared/metis/metis.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -475,50 +475,46 @@ export class MetisService implements OnDestroy {
answer.creationDate = dayjs(answer.creationDate);
});

if (
(postDTO.post.courseWideContext && this.currentPostContextFilter.courseWideContexts?.includes(postDTO.post.courseWideContext)) ||
(postDTO.post.lecture?.id !== undefined && this.currentPostContextFilter.lectureIds?.includes(postDTO.post.lecture.id)) ||
(postDTO.post.exercise?.id !== undefined && this.currentPostContextFilter.exerciseIds?.includes(postDTO.post.exercise.id)) ||
(postDTO.post.conversation?.id !== undefined && postDTO.post.conversation.id === this.currentPostContextFilter.conversationId)
)
switch (postDTO.action) {
case MetisPostAction.CREATE:
// we can add the sent post to the cached posts without violating the current context filter setting
this.cachedPosts.push(postDTO.post);
this.addTags(postDTO.post.tags);
break;
case MetisPostAction.UPDATE:
const indexToUpdate = this.cachedPosts.findIndex((post) => post.id === postDTO.post.id);
if (indexToUpdate > -1) {
this.cachedPosts[indexToUpdate] = postDTO.post;
} else {
console.error(`Post with id ${postDTO.post.id} could not be updated`);
}
this.addTags(postDTO.post.tags);
break;
case MetisPostAction.DELETE:
const indexToDelete = this.cachedPosts.findIndex((post) => post.id === postDTO.post.id);
if (indexToDelete > -1) {
this.cachedPosts.splice(indexToDelete, 1);
} else {
console.error(`Post with id ${postDTO.post.id} could not be deleted`);
}
break;
default:
break;
}
// emit updated version of cachedPosts to subscribing components
switch (postDTO.action) {
case MetisPostAction.CREATE:
if (
postDTO.post.conversation?.id !== undefined &&
postDTO.post.conversation.id === this.currentPostContextFilter.conversationId &&
(!this.currentPostContextFilter.searchText || postDTO.post.content?.toLowerCase().includes(this.currentPostContextFilter.searchText.toLowerCase()))
) {
// we can add the received conversation message to the cached messages without violating the current context filter setting
this.cachedPosts = [postDTO.post, ...this.cachedPosts];
}
this.addTags(postDTO.post.tags);
break;
case MetisPostAction.UPDATE:
const indexToUpdate = this.cachedPosts.findIndex((post) => post.id === postDTO.post.id);
if (indexToUpdate > -1) {
this.cachedPosts[indexToUpdate] = postDTO.post;
}
this.addTags(postDTO.post.tags);
break;
case MetisPostAction.DELETE:
const indexToDelete = this.cachedPosts.findIndex((post) => post.id === postDTO.post.id);
if (indexToDelete > -1) {
this.cachedPosts.splice(indexToDelete, 1);
}
break;
default:
break;
}
// emit updated version of cachedPosts to subscribing components...
if (PageType.OVERVIEW === this.pageType) {
// by invoking the getFilteredPosts method with forceUpdate set to true, i.e. refetch currently displayed posts from server
const oldPage = this.currentPostContextFilter.page;
const oldPageSize = this.currentPostContextFilter.pageSize;
this.currentPostContextFilter.pageSize = oldPageSize! * (oldPage! + 1);
this.currentPostContextFilter.page = 0;
this.getFilteredPosts(this.currentPostContextFilter, true, this.currentConversation);
// ...by invoking the getFilteredPosts method with forceUpdate set to true iff receiving a new Q&A post, i.e. fetching posts from server only in this case
this.getFilteredPosts(this.currentPostContextFilter, !postDTO.post.conversation && postDTO.action === MetisPostAction.CREATE, this.currentConversation);
this.currentPostContextFilter.pageSize = oldPageSize;
this.currentPostContextFilter.page = oldPage;
} else {
// by invoking the getFilteredPosts method with forceUpdate set to false, i.e. without fetching posts from server
// ...by invoking the getFilteredPosts method with forceUpdate set to false, i.e. without fetching posts from server
this.getFilteredPosts(this.currentPostContextFilter, false);
}
});
Expand Down
2 changes: 1 addition & 1 deletion src/main/webapp/content/scss/global.scss
Original file line number Diff line number Diff line change
Expand Up @@ -950,7 +950,7 @@ Course Info Bar
box-shadow: 3px 3px 6px $mat-autocomplete-visible-box-shadow;
}

.mat-select-panel {
.cdk-overlay-container .cdk-overlay-pane .mat-select-panel {
background: $metis-course-discussion-select-bg;
-webkit-box-shadow: 3px 3px 6px $mat-autocomplete-visible-box-shadow;
-moz-box-shadow: 3px 3px 6px $mat-autocomplete-visible-box-shadow;
Expand Down
104 changes: 102 additions & 2 deletions src/test/javascript/spec/service/metis/metis.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { AnswerPost } from 'app/entities/metis/answer-post.model';
import { ReactionService } from 'app/shared/metis/reaction.service';
import { MockReactionService } from '../../helpers/mocks/service/mock-reaction.service';
import { Reaction } from 'app/entities/metis/reaction.model';
import { CourseWideContext, DisplayPriority, MetisPostAction, MetisWebsocketChannelPrefix, PageType } from 'app/shared/metis/metis.util';
import { CourseWideContext, DisplayPriority, MetisPostAction, MetisWebsocketChannelPrefix, PageType, PostContextFilter } from 'app/shared/metis/metis.util';
import { MockTranslateService } from '../../helpers/mocks/service/mock-translate.service';
import { TranslateService } from '@ngx-translate/core';
import { Router } from '@angular/router';
Expand All @@ -23,7 +23,7 @@ import { LocalStorageService, SessionStorageService } from 'ngx-webstorage';
import { MockProvider } from 'ng-mocks';
import { JhiWebsocketService } from 'app/core/websocket/websocket.service';
import { MetisPostDTO } from 'app/entities/metis/metis-post-dto.model';
import { of } from 'rxjs';
import { Subject, of } from 'rxjs';
import {
metisCourse,
metisCoursePostsWithCourseWideContext,
Expand All @@ -42,6 +42,7 @@ import {
import { ITEMS_PER_PAGE } from 'app/shared/constants/pagination.constants';
import { ChannelDTO, ChannelSubType } from 'app/entities/metis/conversation/channel.model';
import { ConversationType } from 'app/entities/metis/conversation/conversation.model';
import { HttpHeaders, HttpResponse } from '@angular/common/http';

describe('Metis Service', () => {
let metisService: MetisService;
Expand Down Expand Up @@ -549,5 +550,104 @@ describe('Metis Service', () => {
isCourseWide: false,
} as ChannelDTO);
}));

it.each([MetisPostAction.CREATE, MetisPostAction.UPDATE, MetisPostAction.DELETE])(
'should not call postService.getPosts() for new or updated messages received over WebSocket',
(action: MetisPostAction) => {
// Setup
const channel = 'someChannel';
const mockPostDTO = {
post: metisPostInChannel,
action,
};
const mockReceiveObservable = new Subject();
websocketServiceReceiveStub.mockReturnValue(mockReceiveObservable.asObservable());
metisService.setPageType(PageType.OVERVIEW);
metisService.createWebsocketSubscription(channel);

// set currentPostContextFilter appropriately
metisService.getFilteredPosts({ conversationId: mockPostDTO.post.conversation?.id } as PostContextFilter);

// Ensure subscribe to websocket was called
expect(websocketService.subscribe).toHaveBeenCalled();

// Emulate receiving a message
const getPostsSpy = jest.spyOn(postService, 'getPosts');
mockReceiveObservable.next(mockPostDTO);

// Ensure getPosts() was not called
expect(getPostsSpy).not.toHaveBeenCalled();
},
);

it('should update displayed conversation messages if new message does not match search text', fakeAsync(() => {
// Setup
const channel = 'someChannel';
const mockPostDTO = {
post: { ...metisPostInChannel, content: 'search Text' },
action: MetisPostAction.CREATE,
};
const mockReceiveObservable = new Subject();
websocketServiceReceiveStub.mockReturnValue(mockReceiveObservable.asObservable());
metisService.setPageType(PageType.OVERVIEW);
metisService.createWebsocketSubscription(channel);

jest.spyOn(postService, 'getPosts').mockReturnValue(
of(
new HttpResponse({
body: [],
headers: new HttpHeaders({
'X-Total-Count': 0,
}),
}),
),
);

// set currentPostContextFilter with search text
metisService.getFilteredPosts({ conversationId: mockPostDTO.post.conversation?.id, searchText: 'Search text' } as PostContextFilter);

// Emulate receiving a message matching the search text
mockReceiveObservable.next(mockPostDTO);
// Emulate receiving a message not matching the search text
mockReceiveObservable.next({
post: { ...metisPostInChannel, content: 'other Text' },
action: MetisPostAction.CREATE,
});

metisService.posts.subscribe((posts) => {
expect(posts[0]).toBe(mockPostDTO.post);
});
tick();
}));

it.each([MetisPostAction.CREATE, MetisPostAction.UPDATE, MetisPostAction.DELETE])(
'should not call postService.getPosts() for new or updated messages received over WebSocket',
(action: MetisPostAction) => {
// Setup
const channel = 'someChannel';
const mockPostDTO = {
post: metisLecturePosts[0],
action,
};
const mockReceiveObservable = new Subject();
websocketServiceReceiveStub.mockReturnValue(mockReceiveObservable.asObservable());
const getPostsSpy = jest.spyOn(postService, 'getPosts');
metisService.setPageType(PageType.OVERVIEW);
metisService.createWebsocketSubscription(channel);

// Ensure subscribe to websocket was called
expect(websocketService.subscribe).toHaveBeenCalled();

// Emulate receiving a post
mockReceiveObservable.next(mockPostDTO);

// Ensure getPosts() was not called
if (action === MetisPostAction.CREATE) {
expect(getPostsSpy).toHaveBeenCalledOnce();
} else {
expect(getPostsSpy).not.toHaveBeenCalled();
}
},
);
});
});

0 comments on commit 705d9ea

Please sign in to comment.