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 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
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 All @@ -50,17 +50,6 @@ public Boolean doesResolvePost() {
return resolvesPost;
}

@Column(name = "has_forwarded_messages")
private boolean hasForwardedMessages;

public boolean getHasForwardedMessages() {
return hasForwardedMessages;
}

public void setHasForwardedMessages(boolean hasForwardedMessages) {
this.hasForwardedMessages = hasForwardedMessages;
}

public void setResolvesPost(Boolean resolvesPost) {
this.resolvesPost = resolvesPost;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,8 @@ public class Post extends Posting {
@Transient
private boolean isSaved = false;

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

public Post() {
}
Expand All @@ -119,14 +119,6 @@ public void setTitle(String title) {
this.title = title;
}

public boolean getHasForwardedMessages() {
return hasForwardedMessages;
}

public void setHasForwardedMessages(boolean hasForwardedMessages) {
this.hasForwardedMessages = hasForwardedMessages;
}

public Boolean isVisibleForStudents() {
return visibleForStudents;
}
Expand Down Expand Up @@ -247,6 +239,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
Expand Up @@ -60,6 +60,9 @@ public abstract class Posting extends DomainObject {
@Transient
private UserRole authorRoleTransient;

@Column(name = "has_forwarded_messages")
private boolean hasForwardedMessages;

public String getTokenizedContent() {
return tokenizedContent;
}
Expand Down Expand Up @@ -121,4 +124,12 @@ public void setAuthorRole(UserRole authorRole) {
public abstract Course getCoursePostingBelongsTo();

public abstract Conversation getConversation();

public boolean getHasForwardedMessages() {
return hasForwardedMessages;
}

public void setHasForwardedMessages(boolean hasForwardedMessages) {
this.hasForwardedMessages = hasForwardedMessages;
}
}
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;
}
Loading
Loading