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

Communication: Add send as direct message option for replies #10420

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 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
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public class AnswerPost extends Posting {
private Set<Reaction> reactions = new HashSet<>();

@ManyToOne
@JsonIncludeProperties({ "id", "exercise", "lecture", "course", "courseWideContext", "conversation", "author" })
@JsonIncludeProperties({ "id", "exercise", "lecture", "course", "courseWideContext", "conversation", "author", "content" })
private Post post;

@Transient
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ public class Post extends Posting {
@Column(name = "has_forwarded_messages")
private boolean hasForwardedMessages;

@Column(name = "original_answer_id")
private Long originalAnswerId;

public Post() {
}

Expand Down Expand Up @@ -247,6 +250,14 @@ public void setIsSaved(boolean isSaved) {
this.isSaved = isSaved;
}

public Long getOriginalAnswerId() {
return originalAnswerId;
}

public void setOriginalAnswerId(Long originalAnswerId) {
this.originalAnswerId = originalAnswerId;
}

/**
* Helper method to extract the course a Post belongs to, which is found in different locations based on the Post's context
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">

<changeSet id="20250227143115" author="aykan">
<addColumn tableName="post">
<column name="original_answer_id" type="BIGINT"/>
</addColumn>

<addForeignKeyConstraint
baseTableName="post"
baseColumnNames="original_answer_id"
constraintName="fk_post_original_answer"
referencedTableName="answer_post"
referencedColumnNames="id"/>
</changeSet>

</databaseChangeLog>
1 change: 1 addition & 0 deletions src/main/resources/config/liquibase/master.xml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
<include file="classpath:config/liquibase/changelog/20250129173003_changelog.xml" relativeToChangelogFile="false"/>
<include file="classpath:config/liquibase/changelog/20250218181818_changelog.xml" relativeToChangelogFile="false"/>
<include file="classpath:config/liquibase/changelog/20250222125010_changelog.xml" relativeToChangelogFile="false"/>
<include file="classpath:config/liquibase/changelog/20250227143115_changelog.xml" relativeToChangelogFile="false"/>

<!-- NOTE: please use the format "YYYYMMDDhhmmss_changelog.xml", i.e. year month day hour minutes seconds and not something else! -->
<!-- we should also stay in a chronological order! -->
Expand Down
2 changes: 2 additions & 0 deletions src/main/webapp/app/entities/metis/post.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ export class Post extends Posting {
public plagiarismCase?: PlagiarismCase;
public displayPriority?: DisplayPriority;
public resolved?: boolean;
public isConsecutive?: boolean = false;
public originalAnswerId?: number;
public forwardedPosts?: Post[] = [];
public forwardedAnswerPosts?: AnswerPost[] = [];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -635,7 +635,15 @@ export class CourseConversationsComponent implements OnInit, OnDestroy {
this.focusPostId = (post as AnswerPost)?.post?.id;
}

this.metisConversationService.setActiveConversation(id);
this.changeDetector.detectChanges();
if (this.activeConversation && this.activeConversation.id === id) {
this.metisConversationService.setActiveConversation(undefined);
setTimeout(() => {
this.metisConversationService.setActiveConversation(id);
this.changeDetector.detectChanges();
}, 0);
} else {
this.metisConversationService.setActiveConversation(id);
this.changeDetector.detectChanges();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
[isFormGroupValid]="formGroup.valid"
[editType]="editType"
/>
<div class="col mt-1 text-end">
<div class="col mt-1">
@if (!warningDismissed) {
<div class="alert alert-warning alert-dismissible text-start fade show" role="alert">
<button type="button" class="btn-close" (click)="closeAlert()" aria-label="Close"></button>
Expand All @@ -22,23 +22,34 @@ <h4 class="alert-heading" jhiTranslate="artemisApp.messageWarning.headerText"></
<p class="mb-0" jhiTranslate="artemisApp.messageWarning.lastParagraph"></p>
</div>
}
@if (editType === EditType.UPDATE) {
<button
jhi-posting-button
[buttonLabel]="'artemisApp.metis.cancel' | artemisTranslate"
class="btn btn-sm btn-outline-secondary"
(click)="isModalOpen.emit()"
></button>
<button
jhi-posting-button
[buttonLoading]="isLoading"
[disabled]="isLoading || !formGroup.valid"
[buttonLabel]="'artemisApp.conversationsLayout.saveMessage' | artemisTranslate"
class="btn btn-sm btn-outline-primary"
id="save"
type="submit"
></button>
}
<div class="d-flex justify-content-between align-items-center">
@if (editType !== EditType.UPDATE) {
<div class="direct-message-checkbox">
<input type="checkbox" id="directMessageCheckbox" [checked]="sendAsDirectMessage()" (change)="toggleSendAsDirectMessage()" />
<label for="directMessageCheckbox" jhiTranslate="artemisApp.conversationsLayout.sendAsDirectMessage"></label>
</div>
}

<div class="d-flex gap-2">
@if (editType === EditType.UPDATE) {
Comment on lines +26 to +34
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think here it would possibly make sense to only use the first condition and then use @else

<button
jhi-posting-button
[buttonLabel]="'artemisApp.metis.cancel' | artemisTranslate"
class="btn btn-sm btn-outline-secondary"
(click)="isModalOpen.emit()"
></button>
<button
jhi-posting-button
[buttonLoading]="isLoading"
[disabled]="isLoading || !formGroup.valid"
[buttonLabel]="'artemisApp.conversationsLayout.saveMessage' | artemisTranslate"
class="btn btn-sm btn-outline-primary"
id="save"
type="submit"
></button>
}
</div>
</div>
</div>
</div>
</form>
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, EventEmitter, OnChanges, OnInit, Output, SimpleChanges, ViewEncapsulation, inject, input } from '@angular/core';
import { Component, EventEmitter, OnChanges, OnInit, Output, SimpleChanges, ViewEncapsulation, inject, input, signal } from '@angular/core';
import { AnswerPost } from 'app/entities/metis/answer-post.model';
import { FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
import { TranslateDirective } from 'app/shared/language/translate.directive';
Expand All @@ -9,6 +9,7 @@ import { PostingMarkdownEditorComponent } from 'app/shared/metis/posting-markdow
import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe';
import { LocalStorageService } from 'ngx-webstorage';
import { ConversationDTO } from 'app/entities/metis/conversation/conversation.model';
import { Post } from 'app/entities/metis/post.model';

@Component({
selector: 'jhi-message-reply-inline-input',
Expand All @@ -26,6 +27,8 @@ export class MessageReplyInlineInputComponent extends PostingCreateEditDirective

@Output() valueChange = new EventEmitter<void>();

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also adapt this here, to use input... ?

sendAsDirectMessage = signal<boolean>(false);

ngOnInit(): void {
super.ngOnInit();
this.warningDismissed = !!this.localStorageService.retrieve('chatWarningDismissed');
Expand All @@ -45,6 +48,10 @@ export class MessageReplyInlineInputComponent extends PostingCreateEditDirective
super.ngOnChanges();
}

toggleSendAsDirectMessage(): void {
this.sendAsDirectMessage.set(!this.sendAsDirectMessage());
}

/**
* resets the answer post content
*/
Expand All @@ -65,18 +72,40 @@ export class MessageReplyInlineInputComponent extends PostingCreateEditDirective
*/
createPosting(): void {
this.posting.content = this.formGroup.get('content')?.value;
this.metisService.createAnswerPost(this.posting).subscribe({
next: (answerPost: AnswerPost) => {
this.resetFormGroup('');
this.isLoading = false;
this.onCreate.emit(answerPost);
},
error: () => {
this.isLoading = false;
this.isLoading = true;

const createAnswerPost$ = this.metisService.createAnswerPost(this.posting);

createAnswerPost$.subscribe({
next: (createdAnswerPost: AnswerPost) => {
if (this.sendAsDirectMessage()) {
const newPost = this.mapAnswerPostToPost(createdAnswerPost);
this.metisService.createPost(newPost).subscribe({
next: () => this.finalizeCreation(createdAnswerPost),
error: () => (this.isLoading = false),
});
} else {
this.finalizeCreation(createdAnswerPost);
}
},
error: () => (this.isLoading = false),
});
}

private finalizeCreation(answerPost: AnswerPost): void {
this.resetFormGroup('');
this.isLoading = false;
this.onCreate.emit(answerPost);
}

private mapAnswerPostToPost(answerPost: AnswerPost): Post {
return {
content: answerPost.content,
conversation: this.activeConversation(),
originalAnswerId: answerPost.id,
} as Post;
}

/**
* invokes the metis service with the updated answer post
* ends the process successfully by closing the modal and stopping the button's loading animation
Expand Down
31 changes: 26 additions & 5 deletions src/main/webapp/app/shared/metis/post/post.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
{{ posting.creationDate | artemisDate: 'time' }}
</span>
}
@if (!isConsecutive()) {
@if (!isConsecutive() || posting.originalAnswerId) {
<div class="ps-3">
<jhi-posting-header
[previewMode]="previewMode()"
Expand All @@ -29,6 +29,18 @@
/>
</div>
}
@if (posting.originalAnswerId) {
<div class="message-container post-content-padding text-muted fs-smaller mt-1">
<div class="message-content">
<span class="flex-wrapper">
<span class="replied-text" jhiTranslate="artemisApp.metis.post.repliedToThread"></span>
<a (click)="onTriggerNavigateToPost(originalAnswer!)" class="one-line">
{{ originalAnswer?.post?.content }}
</a>
</span>
</div>
</div>
}
<div class="align-items-center">
<div>
<div class="post-context-information-wrap post-content-padding" [ngClass]="{ 'is-saved': isConsecutive() && posting.isSaved }">
Expand Down Expand Up @@ -194,10 +206,19 @@
<span jhiTranslate="artemisApp.metis.post.deleteMessage"></span>
</button>
}
<button class="dropdown-item d-flex" (click)="isCommunicationPage ? openThread.emit() : reactionsBarComponent()?.openAnswerView()">
<fa-icon [icon]="faComments" class="item-icon" />
<span jhiTranslate="artemisApp.metis.post.replyMessage"></span>
</button>
@if (!isThreadSidebar) {
@if (!posting.originalAnswerId) {
<button class="dropdown-item d-flex" (click)="isCommunicationPage ? openThread.emit() : reactionsBarComponent()?.openAnswerView()">
<fa-icon [icon]="faComments" class="item-icon" />
<span jhiTranslate="artemisApp.metis.post.replyMessage"></span>
</button>
} @else {
<button class="dropdown-item d-flex" (click)="isCommunicationPage ? onTriggerNavigateToPost(originalAnswer!) : reactionsBarComponent()?.openAnswerView()">
<fa-icon [icon]="faComments" class="item-icon" />
<span jhiTranslate="artemisApp.metis.post.viewThread"></span>
</button>
}
}
<button class="dropdown-item d-flex" (click)="toggleSavePost()">
<fa-icon [icon]="posting.isSaved ? faBookmark : farBookmark" class="item-icon" />
<span [jhiTranslate]="posting.isSaved ? 'artemisApp.metis.post.removeBookmarkPost' : 'artemisApp.metis.post.bookmarkPost'"></span>
Expand Down
20 changes: 20 additions & 0 deletions src/main/webapp/app/shared/metis/post/post.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,23 @@
visibility: visible;
opacity: 1;
}

.flex-wrapper {
display: flex;
align-items: center;
width: 100%;
gap: 0.3rem;
}

.replied-text {
flex: 0 0 auto;
white-space: nowrap;
}

.one-line {
flex: 1 1 0;
min-width: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
19 changes: 19 additions & 0 deletions src/main/webapp/app/shared/metis/post/post.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import { PostingReactionsBarComponent } from 'app/shared/metis/posting-reactions
import { Posting } from 'app/entities/metis/posting.model';
import { throwError } from 'rxjs';
import { ForwardedMessageComponent } from 'app/shared/metis/forwarded-message/forwarded-message.component';
import { AnswerPostService } from 'app/shared/metis/answer-post.service';

@Component({
selector: 'jhi-post',
Expand Down Expand Up @@ -75,6 +76,7 @@ import { ForwardedMessageComponent } from 'app/shared/metis/forwarded-message/fo
})
export class PostComponent extends PostingDirective<Post> implements OnInit, OnChanges, AfterContentChecked {
metisService = inject(MetisService);
answerPostService = inject(AnswerPostService);
changeDetector = inject(ChangeDetectorRef);
renderer = inject(Renderer2);
private document = inject<Document>(DOCUMENT);
Expand Down Expand Up @@ -127,6 +129,7 @@ export class PostComponent extends PostingDirective<Post> implements OnInit, OnC
forwardedPosts = input<Post[]>([]);
forwardedAnswerPosts = input<AnswerPost[]>([]);
dropdownPosition = { x: 0, y: 0 };
originalAnswer?: AnswerPost;
course: Course;

constructor() {
Expand Down Expand Up @@ -219,6 +222,7 @@ export class PostComponent extends PostingDirective<Post> implements OnInit, OnC
this.isAtLeastTutorInCourse = this.metisService.metisUserIsAtLeastTutorInCourse();
this.sortAnswerPosts();
this.assignPostingToPost();
this.loadOriginalAnswerContent();
this.fetchForwardedMessages();
}

Expand Down Expand Up @@ -336,4 +340,19 @@ export class PostComponent extends PostingDirective<Post> implements OnInit, OnC
protected onTriggerNavigateToPost(post: Posting) {
this.onNavigateToPost.emit(post);
}

private loadOriginalAnswerContent(): void {
if (this.posting.originalAnswerId) {
const courseId = this.course.id;
this.answerPostService.getSourceAnswerPostsByIds(courseId!, [this.posting.originalAnswerId]).subscribe({
next: (res) => {
this.originalAnswer = res[0];
this.changeDetector.detectChanges();
},
error: () => {
throwError('AnswerPost not found');
},
});
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div class="d-flex flex-wrap gap-2 fs-xx-small align-items-center" [style.width]="getPostingType() === 'post' ? 'max-content' : ''">
@if (getPostingType() === 'post') {
@if (hoverBar() && sortedAnswerPosts()?.length === 0) {
@if (!originalAnswerId && !isThreadSidebar() && hoverBar() && sortedAnswerPosts()?.length === 0) {
<div>
<button class="reaction-button clickable reply-btn" (click)="isCommunicationPage() ? openThread.emit() : openAnswerView()">
<fa-icon class="fa-xs align-self-center" [icon]="faArrowRight" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ export class PostingReactionsBarComponent<T extends Posting> implements OnInit,
canPin = false;
channels: ChannelDTO[] = [];
users: UserPublicInfoDTO[] = [];
originalAnswerId: number | undefined;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can use this here:

Suggested change
originalAnswerId: number | undefined;
originalAnswerId?: number;

does it even need to be undefined here?

posting = input<T>();
isThreadSidebar = input<boolean>();
isEmojiCount = input<boolean>(false);
Expand Down Expand Up @@ -162,6 +163,7 @@ export class PostingReactionsBarComponent<T extends Posting> implements OnInit,
const currentConversation = this.metisService.getCurrentConversation();
this.setCanPin(currentConversation);
this.resetTooltipsAndPriority();
this.originalAnswerId = (this.posting() as Post).originalAnswerId;
}
this.setMayDelete();
this.setMayEdit();
Expand Down
1 change: 1 addition & 0 deletions src/main/webapp/i18n/de/conversation.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"goToExercise": "Zur Aufgabe gehen",
"goToLecture": "Zur Vorlesung gehen",
"goToExam": "Zur Klausur gehen",
"sendAsDirectMessage": "Auch als Direktnachricht senden",
"conversationMessages": {
"searchLabel": "Nachrichten durchsuchen",
"clearSearch": "Suche löschen",
Expand Down
Loading
Loading