diff --git a/docs/user/communication.rst b/docs/user/communication.rst
index 08ea9763f2d6..96ad2340dbcf 100644
--- a/docs/user/communication.rst
+++ b/docs/user/communication.rst
@@ -64,8 +64,8 @@ Users of a course can communicate in private via the Messages page. (see image b
Conversation sidebar on the left, where users can search for other participants of the current course and start a conversation
with them.
-If the recipient is browsing another conversation when they receive a new message, an envelope icon appears in their
-Conversation sidebar, next to the affiliated user who has sent the message. This way, users become aware of the new message
+If the recipient is browsing another conversation when they receive a new message, an icon with an unread-messages counter is displayed in their
+conversation sidebar, next to the affiliated conversation that the new message was sent to. This way, users become aware of the new message
within that discussion.
The authorities of tutors and instructors are more restricted in the Messages Page compared to the Course Communication
@@ -78,6 +78,17 @@ located on the right-hand side of the Messages Page when displayed.
|messages|
+If the message content contains links, a preview of the link will be shown under the message. This way, users will have a good understanding
+of what that link is about. If they prefer not to have a preview, they can hover over the preview and click the appearing `X` button.
+The preview will be removed.
+
+|link-preview|
+
+If the message contains more than one link, the preview will not have a preview image of the link to have more compact previews
+for multiple links.
+
+|link-preview-multiple|
+
Features for Users
------------------
@@ -182,6 +193,27 @@ Reference Lecture Attachments
Users can refer to lectures of the current course, via the dropdown menu ``Lecture`` available on the posting markdown
editor (see image above). Here, lecture attachments can be found in a nested structure.
+Reference Lecture Attachment Units
+""""""""""""""""""""""""""""""""""
+
+Users can refer to lecture attachment units of the current course, via the dropdown menu ``Lecture`` available on the posting markdown
+editor, see image below. Here, lecture attachment units can be found when users hover over the specific lecture.
+
+Reference Lecture Unit Slides
+"""""""""""""""""""""""""""""
+
+Users can refer to lecture unit slides of the current course, via the dropdown menu ``Lecture``. Here, slides can be found when users
+hover over a specific unit, see image below.
+
+|slide-reference-menu|
+
+After the user references a single slide they can see it as an image included in the message. Additionally, they can preview the slide
+in order to easily read the content by clicking the image.
+
+|slide-reference|
+
+|referenced-slide-preview|
+
Prevent Post Duplication
^^^^^^^^^^^^^^^^^^^^^^^^
@@ -267,3 +299,13 @@ Additionally, announcements visually differ from normal posts and are always dis
:width: 600
.. |messages| image:: communication/messages.png
:width: 1000
+.. |slide-reference| image:: communication/slide-reference.png
+ :width: 600
+.. |slide-reference-menu| image:: communication/slide-reference-menu.png
+ :width: 1000
+.. |referenced-slide-preview| image:: communication/referenced-slide-preview.png
+ :width: 600
+.. |link-preview| image:: communication/link-preview.png
+ :width: 600
+.. |link-preview-multiple| image:: communication/link-preview-multiple.png
+ :width: 600
diff --git a/docs/user/communication/link-preview-multiple.png b/docs/user/communication/link-preview-multiple.png
new file mode 100644
index 000000000000..2ba7d9c7f9ad
Binary files /dev/null and b/docs/user/communication/link-preview-multiple.png differ
diff --git a/docs/user/communication/link-preview.png b/docs/user/communication/link-preview.png
new file mode 100644
index 000000000000..4c0d2571841c
Binary files /dev/null and b/docs/user/communication/link-preview.png differ
diff --git a/docs/user/communication/messages.png b/docs/user/communication/messages.png
index ce981e7fb0a4..5b32935cff95 100644
Binary files a/docs/user/communication/messages.png and b/docs/user/communication/messages.png differ
diff --git a/docs/user/communication/referenced-slide-preview.png b/docs/user/communication/referenced-slide-preview.png
new file mode 100644
index 000000000000..196aa34804c5
Binary files /dev/null and b/docs/user/communication/referenced-slide-preview.png differ
diff --git a/docs/user/communication/slide-reference-menu.png b/docs/user/communication/slide-reference-menu.png
new file mode 100644
index 000000000000..96f7876129cc
Binary files /dev/null and b/docs/user/communication/slide-reference-menu.png differ
diff --git a/docs/user/communication/slide-reference.png b/docs/user/communication/slide-reference.png
new file mode 100644
index 000000000000..89a07ae59a62
Binary files /dev/null and b/docs/user/communication/slide-reference.png differ
diff --git a/src/main/java/de/tum/in/www1/artemis/service/metis/AnswerMessageService.java b/src/main/java/de/tum/in/www1/artemis/service/metis/AnswerMessageService.java
index d5b18bbcb5fe..fcde432faa74 100644
--- a/src/main/java/de/tum/in/www1/artemis/service/metis/AnswerMessageService.java
+++ b/src/main/java/de/tum/in/www1/artemis/service/metis/AnswerMessageService.java
@@ -1,5 +1,6 @@
package de.tum.in.www1.artemis.service.metis;
+import java.time.ZonedDateTime;
import java.util.Objects;
import java.util.Set;
@@ -144,6 +145,8 @@ public AnswerPost updateAnswerMessage(Long courseId, Long answerMessageId, Answe
existingAnswerMessage.setContent(answerMessage.getContent());
}
+ existingAnswerMessage.setUpdatedDate(ZonedDateTime.now());
+
updatedAnswerMessage = answerPostRepository.save(existingAnswerMessage);
updatedAnswerMessage.getPost().setConversation(conversation);
diff --git a/src/main/webapp/app/course/manage/detail/course-detail-doughnut-chart.component.html b/src/main/webapp/app/course/manage/detail/course-detail-doughnut-chart.component.html
index 4b71fbcaa991..04154e31f3aa 100644
--- a/src/main/webapp/app/course/manage/detail/course-detail-doughnut-chart.component.html
+++ b/src/main/webapp/app/course/manage/detail/course-detail-doughnut-chart.component.html
@@ -1,8 +1,8 @@
diff --git a/src/main/webapp/app/course/manage/detail/course-detail-line-chart.component.html b/src/main/webapp/app/course/manage/detail/course-detail-line-chart.component.html
index 6809a04ba06d..2a506ff76fa3 100644
--- a/src/main/webapp/app/course/manage/detail/course-detail-line-chart.component.html
+++ b/src/main/webapp/app/course/manage/detail/course-detail-line-chart.component.html
@@ -20,7 +20,7 @@ {{ 'artemisApp.courseStatistics.activeStudents' | artemisTranslate }}
>
Course Details:
{{ course.shortName }}
+
+ Course Organizations
+
+ {{ organization.name }}
+
+
diff --git a/src/main/webapp/app/shared/link-preview/components/link-preview/link-preview.component.scss b/src/main/webapp/app/shared/link-preview/components/link-preview/link-preview.component.scss
index ae09d5a3381d..99070c295cfd 100644
--- a/src/main/webapp/app/shared/link-preview/components/link-preview/link-preview.component.scss
+++ b/src/main/webapp/app/shared/link-preview/components/link-preview/link-preview.component.scss
@@ -30,7 +30,7 @@
.loading-container,
.preview-content {
- margin-left: 10px;
+ margin-left: 5px;
max-width: 600px;
/* Add responsive styles */
@@ -59,11 +59,13 @@
.preview-container {
margin-top: 20px;
margin-bottom: 10px;
+ margin-left: 10px;
}
.preview-card {
flex-direction: row;
padding-left: 10px;
+ margin-left: 2px;
position: relative;
}
@@ -83,4 +85,19 @@
max-height: 150px;
margin-right: 10px;
margin-top: 10px;
+ border-radius: 4px;
+}
+
+.close-button {
+ position: absolute;
+ top: 0;
+ left: -12px;
+ font-weight: bold;
+ cursor: pointer;
+ opacity: 0; /* Initially hide the button */
+ transition: opacity 0.3s; /* Add a smooth transition effect */
+}
+
+.preview-card:hover .close-button {
+ opacity: 1; /* Show the button on hover */
}
diff --git a/src/main/webapp/app/shared/link-preview/components/link-preview/link-preview.component.ts b/src/main/webapp/app/shared/link-preview/components/link-preview/link-preview.component.ts
index e4f0ff350f1b..0da0625bcc0d 100644
--- a/src/main/webapp/app/shared/link-preview/components/link-preview/link-preview.component.ts
+++ b/src/main/webapp/app/shared/link-preview/components/link-preview/link-preview.component.ts
@@ -1,15 +1,68 @@
-import { Component, Input } from '@angular/core';
+import { Component, Input, OnInit } from '@angular/core';
import { LinkPreview } from 'app/shared/link-preview/services/link-preview.service';
+import { faTimes } from '@fortawesome/free-solid-svg-icons';
+import { MetisService } from 'app/shared/metis/metis.service';
+import { Posting } from 'app/entities/metis/posting.model';
+import { urlRegex } from 'app/shared/link-preview/services/linkify.service';
@Component({
selector: 'jhi-link-preview',
templateUrl: './link-preview.component.html',
styleUrls: ['./link-preview.component.scss'],
})
-export class LinkPreviewComponent {
+export class LinkPreviewComponent implements OnInit {
@Input() linkPreview: LinkPreview;
@Input() showLoadingsProgress: boolean;
@Input() loaded: boolean;
@Input() hasError: boolean;
- @Input() multiple: boolean;
+ @Input() posting?: Posting;
+ @Input() isReply?: boolean;
+ @Input() multiple?: boolean;
+
+ isAuthorOfOriginalPost: boolean;
+
+ faTimes = faTimes;
+
+ constructor(private metisService: MetisService) {}
+
+ ngOnInit() {
+ this.isAuthorOfOriginalPost = this.metisService.metisUserIsAuthorOfPosting(this.posting!);
+ }
+
+ /**
+ * Removes the link preview from the list of link previews
+ *
+ * @param {LinkPreview} linkPreview the link preview to be removed
+ */
+ removeLinkPreview(linkPreview: LinkPreview) {
+ const urlToSearchFor = linkPreview.url;
+
+ if (this.posting) {
+ // Find all URL matches in the text (in the content of the post)
+ let match;
+ let modifiedContent = this.posting.content!;
+ while ((match = urlRegex.exec(modifiedContent)) !== null) {
+ const url = match[0];
+ const start = match.index;
+ const end = start + url.length;
+
+ if (url === urlToSearchFor || url.includes(urlToSearchFor)) {
+ // wrap the URL in <>
+ modifiedContent = modifiedContent.substring(0, start) + `<${url}>` + modifiedContent.substring(end);
+ }
+ }
+
+ this.posting.content = modifiedContent;
+
+ if (this.isReply) {
+ this.metisService.updateAnswerPost(this.posting).subscribe({
+ next: () => {},
+ });
+ } else {
+ this.metisService.updatePost(this.posting).subscribe({
+ next: () => {},
+ });
+ }
+ }
+ }
}
diff --git a/src/main/webapp/app/shared/link-preview/services/linkify.service.ts b/src/main/webapp/app/shared/link-preview/services/linkify.service.ts
index 8e70005feba1..da6397cb28dd 100644
--- a/src/main/webapp/app/shared/link-preview/services/linkify.service.ts
+++ b/src/main/webapp/app/shared/link-preview/services/linkify.service.ts
@@ -7,8 +7,13 @@ export interface Link {
href: string;
start?: number;
end?: number;
+ isLinkPreviewRemoved?: boolean;
}
+// Regular expression pattern to match URLs
+// eslint-disable-next-line no-useless-escape
+export const urlRegex = /https?:\/\/[^\s/$.?#>][^\s>]*?(?=\s|[\]\)]|$)/g;
+
@Injectable()
export class LinkifyService {
/**
@@ -19,9 +24,6 @@ export class LinkifyService {
find(text: string): Link[] {
const linkableItems: Link[] = [];
- // Regular expression pattern to match URLs
- const urlRegex = /https?:\/\/[^\s/$.?#].[^\s]*/g;
-
// Find all URL matches in the text (in the content of the post)
let match;
while ((match = urlRegex.exec(text)) !== null) {
@@ -29,18 +31,22 @@ export class LinkifyService {
const start = match.index;
const end = start + url.length;
- const linkableItem = {
+ // Check if url is wrapped in <> tags
+ const isRemoved = text[start - 1] === '<' && text[end] === '>';
+ const linkableItem: Link = {
type: 'url',
value: url,
isLink: true,
href: url,
start,
end,
+ isLinkPreviewRemoved: isRemoved,
};
- linkableItems.push(linkableItem);
+ if (!isRemoved) {
+ linkableItems.push(linkableItem);
+ }
}
-
return linkableItems;
}
}
diff --git a/src/main/webapp/app/shared/metis/answer-post/answer-post.component.html b/src/main/webapp/app/shared/metis/answer-post/answer-post.component.html
index 000d15eb09ca..5d5c76717c04 100644
--- a/src/main/webapp/app/shared/metis/answer-post/answer-post.component.html
+++ b/src/main/webapp/app/shared/metis/answer-post/answer-post.component.html
@@ -12,6 +12,9 @@
*ngIf="!createAnswerPostModal.isInputOpen"
[content]="posting.content"
[isEdited]="!!posting.updatedDate"
+ [author]="posting.author"
+ [posting]="posting"
+ [isReply]="true"
(userReferenceClicked)="userReferenceClicked.emit($event)"
>
diff --git a/src/main/webapp/app/shared/metis/post/post.component.html b/src/main/webapp/app/shared/metis/post/post.component.html
index 358f910558c1..490d81c41a4b 100644
--- a/src/main/webapp/app/shared/metis/post/post.component.html
+++ b/src/main/webapp/app/shared/metis/post/post.component.html
@@ -59,7 +59,10 @@
*ngIf="!displayInlineInput"
[previewMode]="previewMode"
[content]="posting.content"
+ [author]="posting.author"
[isEdited]="!!posting.updatedDate"
+ [posting]="posting"
+ [isReply]="false"
[isAnnouncement]="posting.courseWideContext === CourseWideContext.ANNOUNCEMENT"
(userReferenceClicked)="onUserReferenceClicked($event)"
>
diff --git a/src/main/webapp/app/shared/metis/posting-content/posting-content.component.html b/src/main/webapp/app/shared/metis/posting-content/posting-content.component.html
index e994fadea721..6602520b5225 100644
--- a/src/main/webapp/app/shared/metis/posting-content/posting-content.component.html
+++ b/src/main/webapp/app/shared/metis/posting-content/posting-content.component.html
@@ -27,6 +27,6 @@
>
{{ 'artemisApp.metis.edited' | artemisTranslate }}
-
+
diff --git a/src/main/webapp/app/shared/metis/posting-content/posting-content.components.ts b/src/main/webapp/app/shared/metis/posting-content/posting-content.components.ts
index 95e5bc673c52..523a20017309 100644
--- a/src/main/webapp/app/shared/metis/posting-content/posting-content.components.ts
+++ b/src/main/webapp/app/shared/metis/posting-content/posting-content.components.ts
@@ -5,6 +5,8 @@ import { Post } from 'app/entities/metis/post.model';
import { MetisService } from 'app/shared/metis/metis.service';
import { Subscription } from 'rxjs';
import { PatternMatch, PostingContentPart, ReferenceType } from '../metis.util';
+import { User } from 'app/core/user/user.model';
+import { Posting } from 'app/entities/metis/posting.model';
@Component({
selector: 'jhi-posting-content',
@@ -15,8 +17,10 @@ export class PostingContentComponent implements OnInit, OnChanges, OnDestroy {
@Input() content?: string;
@Input() previewMode?: boolean;
@Input() isAnnouncement = false;
+ @Input() author?: User;
@Input() isEdited = false;
-
+ @Input() posting?: Posting;
+ @Input() isReply?: boolean;
@Output() userReferenceClicked = new EventEmitter();
showContent = false;
diff --git a/src/main/webapp/i18n/de/course.json b/src/main/webapp/i18n/de/course.json
index 642561a24be8..736e7f87c5f0 100644
--- a/src/main/webapp/i18n/de/course.json
+++ b/src/main/webapp/i18n/de/course.json
@@ -93,7 +93,8 @@
},
"messagingEnabled": {
"label": "Nachrichten aktiviert",
- "tooltip": "Ermöglicht den Nachrichtenaustausch zwischen Nutzer:innen in privaten oder öffentlichen Kanälen, Gruppenchats oder Direktnachrichten. Kanäle können nur von Lehrenden und Tutor:innen erstellt werden. Nutzer:innen können selbst öffentlichen Kanälen beitreten und müssen zu privaten Kanälen hinzugefügt werden. Alle Nutzer:innen können einen privaten Gruppenchat starten und andere Nutzer:innen hinzufügen. Ein Gruppenchat ist auf zehn Mitglieder:innen begrenzt. Alle Nutzer:innen können Direktnachrichten an andere Nutzer:innen senden. Die Chats finden im Nachrichtenbereich des Kurses statt."
+ "tooltip": "Ermöglicht den Nachrichtenaustausch zwischen Nutzer:innen in privaten oder öffentlichen Kanälen, Gruppenchats oder Direktnachrichten. Kanäle können nur von Lehrenden und Tutor:innen erstellt werden. Nutzer:innen können selbst öffentlichen Kanälen beitreten und müssen zu privaten Kanälen hinzugefügt werden. Alle Nutzer:innen können einen privaten Gruppenchat starten und andere Nutzer:innen hinzufügen. Ein Gruppenchat ist auf zehn Mitglieder:innen begrenzt. Alle Nutzer:innen können Direktnachrichten an andere Nutzer:innen senden. Die Chats finden im Nachrichtenbereich des Kurses statt.",
+ "codeOfConduct": "Nachrichten: Code of Conduct"
}
},
"registrationEnabled": {
diff --git a/src/main/webapp/i18n/de/metis.json b/src/main/webapp/i18n/de/metis.json
index f4d598cc511b..a947eaddb329 100644
--- a/src/main/webapp/i18n/de/metis.json
+++ b/src/main/webapp/i18n/de/metis.json
@@ -18,6 +18,8 @@
"confirmDeletePost": "Bitte bestätige, dass du diesen Beitrag löschen möchtest!",
"deleteAnswer": "Möchtest du diese Antwort löschen?",
"confirmDeleteAnswer": "Bitte bestätige, dass du diese Antwort löschen möchtest!",
+ "removeLinkPreview": "Möchten Sie die Vorschau entfernen?",
+ "confirmRemoveLinkPreview": "Bitte bestätigen, dass du die Vorschau entfernen möchtest!",
"createModalTitlePost": "Beitrag erstellen",
"createModalTitleAnswer": "Antwort erstellen",
"editPosting": "Inhalt bearbeiten",
diff --git a/src/main/webapp/i18n/en/course.json b/src/main/webapp/i18n/en/course.json
index 978785393729..7454a6b08cfa 100644
--- a/src/main/webapp/i18n/en/course.json
+++ b/src/main/webapp/i18n/en/course.json
@@ -69,7 +69,7 @@
"title": "Maximum number of points for course",
"info": "This value is used for example calculations (e.g. in the grading key) and does not influence the students' grades. The grades are calculated based on the points achievable in the course."
},
- "accuracyOfScores": "Amount of decimal places used for calculating the scores",
+ "accuracyOfScores": "Number of decimal places used for calculating the scores",
"defaultProgrammingLanguage": "Default Programming Language",
"gradingSystem": "Grading Key",
"testCourse": {
@@ -93,7 +93,8 @@
},
"messagingEnabled": {
"label": "Messaging Enabled",
- "tooltip": "Enables messaging between course users in private or public channels, group chats or direct messages. Channels can only be created by instructors and tutors. Users can self-join public channels and must be invited to private channels. Every user can start a private group chat and add other users. A group chat is limited to 10 members. Every user can start a private one-to-one chat with another user. The chats happens in the messaging space of the course."
+ "tooltip": "Enables messaging between course users in private or public channels, group chats or direct messages. Channels can only be created by instructors and tutors. Users can self-join public channels and must be invited to private channels. Every user can start a private group chat and add other users. A group chat is limited to 10 members. Every user can start a private one-to-one chat with another user. The chats happens in the messaging space of the course.",
+ "codeOfConduct": "Messaging Code of Conduct"
}
},
"registrationEnabled": {
diff --git a/src/main/webapp/i18n/en/metis.json b/src/main/webapp/i18n/en/metis.json
index 3e1f3061d345..03736a9fe8d6 100644
--- a/src/main/webapp/i18n/en/metis.json
+++ b/src/main/webapp/i18n/en/metis.json
@@ -16,6 +16,8 @@
"toggleThread": "Click to toggle the thread",
"deletePost": "Do you want to delete your post?",
"confirmDeletePost": "Please confirm that you want to delete this post!",
+ "removeLinkPreview": "Do you want to remove the preview?",
+ "confirmRemoveLinkPreview": "Please confirm that you want to remove the preview!",
"deleteAnswer": "Do you want to delete this reply?",
"confirmDeleteAnswer": "Please confirm that you want to delete this reply!",
"createModalTitlePost": "Create post",
diff --git a/src/test/javascript/spec/component/link-preview/link-preview.component.spec.ts b/src/test/javascript/spec/component/link-preview/link-preview.component.spec.ts
index 4352c5998dd7..022a0efcb96f 100644
--- a/src/test/javascript/spec/component/link-preview/link-preview.component.spec.ts
+++ b/src/test/javascript/spec/component/link-preview/link-preview.component.spec.ts
@@ -1,20 +1,38 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { LinkPreviewComponent } from 'app/shared/link-preview/components/link-preview/link-preview.component';
+import { MetisService } from 'app/shared/metis/metis.service';
+import { MockTranslateService } from '../../helpers/mocks/service/mock-translate.service';
+import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe';
+import { MockComponent, MockPipe } from 'ng-mocks';
+import { TranslateService } from '@ngx-translate/core';
+import { LocalStorageService, SessionStorageService } from 'ngx-webstorage';
+import { MockSyncStorage } from '../../helpers/mocks/service/mock-sync-storage.service';
+import { MockMetisService } from '../../helpers/mocks/service/mock-metis-service.service';
+import { ConfirmIconComponent } from 'app/shared/confirm-icon/confirm-icon.component';
+import { Post } from 'app/entities/metis/post.model';
+import { AnswerPost } from 'app/entities/metis/answer-post.model';
describe('LinkPreviewComponent', () => {
let component: LinkPreviewComponent;
let fixture: ComponentFixture;
+ let metisService: MetisService;
beforeEach(() => {
TestBed.configureTestingModule({
- declarations: [LinkPreviewComponent],
- })
- .compileComponents()
- .then(() => {
- fixture = TestBed.createComponent(LinkPreviewComponent);
- component = fixture.componentInstance;
- fixture.detectChanges();
- });
+ declarations: [LinkPreviewComponent, MockPipe(ArtemisTranslatePipe), MockComponent(ConfirmIconComponent)],
+ providers: [
+ { provide: MetisService, useClass: MockMetisService },
+ { provide: TranslateService, useClass: MockTranslateService },
+ { provide: SessionStorageService, useClass: MockSyncStorage },
+ { provide: LocalStorageService, useClass: MockSyncStorage },
+ ], // Add any required dependencies here
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(LinkPreviewComponent);
+ metisService = TestBed.inject(MetisService);
+ component = fixture.componentInstance;
+ component.posting = new Post(); // Set up a dummy Posting object if required
+ fixture.detectChanges();
});
it('should create', () => {
@@ -105,4 +123,54 @@ describe('LinkPreviewComponent', () => {
expect(errorContainer).toBeFalsy();
});
+
+ it('should initialize isAuthorOfOriginalPost', () => {
+ // Modify the metisService to return a desired value for metisUserIsAuthorOfPosting
+ const metisServiceSpy = jest.spyOn(metisService, 'metisUserIsAuthorOfPosting').mockReturnValue(true);
+
+ component.ngOnInit();
+
+ expect(component.isAuthorOfOriginalPost).toBeTrue();
+ expect(metisServiceSpy).toHaveBeenCalled();
+ });
+
+ it('should remove link preview from message', () => {
+ const linkPreview: any = {
+ url: 'https://example.com',
+ };
+
+ component.isReply = false;
+ component.posting = new Post();
+ component.posting.content = 'This is a sample post with a link: https://example.com';
+
+ const metisServiceSpy = jest.spyOn(metisService, 'metisUserIsAuthorOfPosting').mockReturnValue(true);
+ const metisServiceUpdatePostSpy = jest.spyOn(metisService, 'updatePost');
+
+ component.ngOnInit();
+ component.removeLinkPreview(linkPreview);
+
+ expect(metisServiceSpy).toHaveBeenCalled();
+ expect(metisServiceUpdatePostSpy).toHaveBeenCalled();
+ expect(component.posting.content).toContain('');
+ });
+
+ it('should remove link preview from reply', () => {
+ const linkPreview: any = {
+ url: 'https://example.com',
+ };
+
+ component.isReply = true;
+ component.posting = new AnswerPost();
+ component.posting.content = 'This is a sample answer post with a link: https://example.com';
+
+ const metisServiceSpy = jest.spyOn(metisService, 'metisUserIsAuthorOfPosting').mockReturnValue(true);
+ const metisServiceUpdateAnswerPostSpy = jest.spyOn(metisService, 'updateAnswerPost');
+
+ component.ngOnInit();
+ component.removeLinkPreview(linkPreview);
+
+ expect(metisServiceSpy).toHaveBeenCalled();
+ expect(metisServiceUpdateAnswerPostSpy).toHaveBeenCalled();
+ expect(component.posting.content).toContain('');
+ });
});
diff --git a/src/test/javascript/spec/component/link-preview/linkify-service.spec.ts b/src/test/javascript/spec/component/link-preview/linkify-service.spec.ts
index 1b63191ff328..4761dd5ca7a2 100644
--- a/src/test/javascript/spec/component/link-preview/linkify-service.spec.ts
+++ b/src/test/javascript/spec/component/link-preview/linkify-service.spec.ts
@@ -1,5 +1,5 @@
import { TestBed } from '@angular/core/testing';
-import { LinkifyService } from 'app/shared/link-preview/services/linkify.service';
+import { Link, LinkifyService } from 'app/shared/link-preview/services/linkify.service';
describe('LinkifyService', () => {
let service: LinkifyService;
@@ -13,7 +13,7 @@ describe('LinkifyService', () => {
it('find should return array of links in the given text', () => {
const text = 'Check out this link: https://example.com';
- const expectedLinks = [
+ const expectedLinks: Link[] = [
{
type: 'url',
value: 'https://example.com',
@@ -21,10 +21,19 @@ describe('LinkifyService', () => {
isLink: true,
end: 40,
start: 21,
+ isLinkPreviewRemoved: false,
},
];
const links = service.find(text);
expect(links).toEqual(expectedLinks);
});
+
+ it('should mark isLinkPreviewRemoved to true when links are wrapped with <>', () => {
+ const text = 'Check out this link: ';
+ const expectedLinks: Link[] = []; // should be empty because link preview is removed
+
+ const links = service.find(text);
+ expect(links).toEqual(expectedLinks);
+ });
});