diff --git a/src/main/java/de/tum/cit/aet/artemis/core/dto/UserPublicInfoDTO.java b/src/main/java/de/tum/cit/aet/artemis/core/dto/UserPublicInfoDTO.java
index a6da8966dfc5..f84bf9e0819a 100644
--- a/src/main/java/de/tum/cit/aet/artemis/core/dto/UserPublicInfoDTO.java
+++ b/src/main/java/de/tum/cit/aet/artemis/core/dto/UserPublicInfoDTO.java
@@ -25,6 +25,8 @@ public class UserPublicInfoDTO {
private String lastName;
+ private String imageUrl;
+
private Boolean isInstructor;
private Boolean isEditor;
@@ -43,6 +45,7 @@ public UserPublicInfoDTO(User user) {
this.name = user.getName();
this.firstName = user.getFirstName();
this.lastName = user.getLastName();
+ this.imageUrl = user.getImageUrl();
}
/**
@@ -101,6 +104,14 @@ public void setLastName(String lastName) {
this.lastName = lastName;
}
+ public String getImageUrl() {
+ return imageUrl;
+ }
+
+ public void setImageUrl(String imageUrl) {
+ this.imageUrl = imageUrl;
+ }
+
public Boolean getIsInstructor() {
return isInstructor;
}
@@ -152,6 +163,7 @@ public int hashCode() {
@Override
public String toString() {
return "UserPublicInfoDTO{" + "id=" + id + ", login='" + login + '\'' + ", name='" + name + '\'' + ", firstName='" + firstName + '\'' + ", lastName='" + lastName + '\''
- + ", isInstructor=" + isInstructor + ", isEditor=" + isEditor + ", isTeachingAssistant=" + isTeachingAssistant + ", isStudent=" + isStudent + '}';
+ + ", imageUrl='" + imageUrl + '\'' + ", isInstructor=" + isInstructor + ", isEditor=" + isEditor + ", isTeachingAssistant=" + isTeachingAssistant + ", isStudent="
+ + isStudent + '}';
}
}
diff --git a/src/main/webapp/app/core/user/user.model.ts b/src/main/webapp/app/core/user/user.model.ts
index 816cf4fc9a9c..b55ff839fd54 100644
--- a/src/main/webapp/app/core/user/user.model.ts
+++ b/src/main/webapp/app/core/user/user.model.ts
@@ -66,6 +66,7 @@ export class UserPublicInfoDTO {
public firstName?: string;
public lastName?: string;
public email?: string;
+ public imageUrl?: string;
public isInstructor?: boolean;
public isEditor?: boolean;
public isTeachingAssistant?: boolean;
diff --git a/src/main/webapp/app/overview/course-conversations/course-wide-search/course-wide-search.component.html b/src/main/webapp/app/overview/course-conversations/course-wide-search/course-wide-search.component.html
index f4a255b49422..aa3b35159b88 100644
--- a/src/main/webapp/app/overview/course-conversations/course-wide-search/course-wide-search.component.html
+++ b/src/main/webapp/app/overview/course-conversations/course-wide-search/course-wide-search.component.html
@@ -3,9 +3,9 @@
-
+ @if (userImageUrl) {
+
+ } @else {
+ {{ userInitials }}
+ }
@if (isChannel(activeConversation) && conversationMember?.isChannelModerator) {
}
{{ userLabel }}
+ @if (!conversationMember.isStudent) {
+
+ }
@if (canBeRemovedFromConversation || canBeGrantedChannelModeratorRole || canBeRevokedChannelModeratorRole) {
diff --git a/src/main/webapp/app/overview/course-conversations/dialogs/conversation-detail-dialog/tabs/conversation-members/conversation-member-row/conversation-member-row.component.scss b/src/main/webapp/app/overview/course-conversations/dialogs/conversation-detail-dialog/tabs/conversation-members/conversation-member-row/conversation-member-row.component.scss
index 28814b8391f5..a2f66745bed6 100644
--- a/src/main/webapp/app/overview/course-conversations/dialogs/conversation-detail-dialog/tabs/conversation-members/conversation-member-row/conversation-member-row.component.scss
+++ b/src/main/webapp/app/overview/course-conversations/dialogs/conversation-detail-dialog/tabs/conversation-members/conversation-member-row/conversation-member-row.component.scss
@@ -1,3 +1,5 @@
+$profile-picture-height: 2rem;
+
.conversation-member-row {
min-height: 3rem;
@@ -14,4 +16,21 @@
.dropdown-toggle::after {
content: none;
}
+
+ .conversation-member-row-default-profile-picture {
+ font-size: 0.8rem;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+ .conversation-member-row-profile-picture,
+ .conversation-member-row-default-profile-picture {
+ width: $profile-picture-height;
+ height: $profile-picture-height;
+ max-width: $profile-picture-height;
+ max-height: $profile-picture-height;
+ background-color: var(--gray-400);
+ color: var(--white);
+ }
}
diff --git a/src/main/webapp/app/overview/course-conversations/dialogs/conversation-detail-dialog/tabs/conversation-members/conversation-member-row/conversation-member-row.component.ts b/src/main/webapp/app/overview/course-conversations/dialogs/conversation-detail-dialog/tabs/conversation-members/conversation-member-row/conversation-member-row.component.ts
index 5044543d4878..39a712c64424 100644
--- a/src/main/webapp/app/overview/course-conversations/dialogs/conversation-detail-dialog/tabs/conversation-members/conversation-member-row/conversation-member-row.component.ts
+++ b/src/main/webapp/app/overview/course-conversations/dialogs/conversation-detail-dialog/tabs/conversation-members/conversation-member-row/conversation-member-row.component.ts
@@ -1,5 +1,5 @@
import { Component, EventEmitter, HostBinding, Input, OnDestroy, OnInit, Output } from '@angular/core';
-import { faChalkboardTeacher, faEllipsis, faUser, faUserCheck, faUserGear } from '@fortawesome/free-solid-svg-icons';
+import { faEllipsis, faUser, faUserCheck, faUserGear, faUserGraduate } from '@fortawesome/free-solid-svg-icons';
import { User } from 'app/core/user/user.model';
import { ConversationDTO } from 'app/entities/metis/conversation/conversation.model';
import { AccountService } from 'app/core/auth/account.service';
@@ -20,6 +20,8 @@ import { HttpErrorResponse, HttpResponse } from '@angular/common/http';
import { getAsGroupChatDTO, isGroupChatDTO } from 'app/entities/metis/conversation/group-chat.model';
import { GroupChatService } from 'app/shared/metis/conversations/group-chat.service';
import { catchError } from 'rxjs/operators';
+import { getBackgroundColorHue } from 'app/utils/color.utils';
+import { getInitialsFromString } from 'app/utils/text.utils';
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
@@ -56,6 +58,9 @@ export class ConversationMemberRowComponent implements OnInit, OnDestroy {
canBeRevokedChannelModeratorRole = false;
userLabel: string;
+ userImageUrl: string | undefined;
+ userDefaultPictureHue: string;
+ userInitials: string;
// icons
userIcon: IconProp = faUser;
userTooltip = '';
@@ -88,7 +93,10 @@ export class ConversationMemberRowComponent implements OnInit, OnDestroy {
this.isCreator = true;
}
+ this.userImageUrl = this.conversationMember.imageUrl;
this.userLabel = getUserLabel(this.conversationMember);
+ this.userInitials = getInitialsFromString(this.conversationMember.name ?? 'NA');
+ this.userDefaultPictureHue = getBackgroundColorHue(this.conversationMember.id ? this.conversationMember.id.toString() : 'default');
this.setUserAuthorityIconAndTooltip();
// the creator of a channel can not be removed from the channel
this.canBeRemovedFromConversation = !this.isCurrentUser && this.canRemoveUsersFromConversation(this.activeConversation);
@@ -242,7 +250,7 @@ export class ConversationMemberRowComponent implements OnInit, OnDestroy {
const toolTipTranslationPath = 'artemisApp.metis.userAuthorityTooltips.';
// highest authority is displayed
if (this.conversationMember.isInstructor) {
- this.userIcon = faChalkboardTeacher;
+ this.userIcon = faUserGraduate;
this.userTooltip = this.translateService.instant(toolTipTranslationPath + 'instructor');
} else if (this.conversationMember.isEditor || this.conversationMember.isTeachingAssistant) {
this.userIcon = faUserCheck;
diff --git a/src/main/webapp/i18n/de/metis.json b/src/main/webapp/i18n/de/metis.json
index 5100a48cd2e4..8a669a9c8c8c 100644
--- a/src/main/webapp/i18n/de/metis.json
+++ b/src/main/webapp/i18n/de/metis.json
@@ -90,7 +90,9 @@
"TECH_SUPPORT": "Technische Hilfe",
"ORGANIZATION": "Organisation",
"RANDOM": "Sonstiges",
- "ANNOUNCEMENT": "Ankündigung"
+ "ANNOUNCEMENT": "Ankündigung",
+ "allPublicMessages": "Alle öffentlichen Nachrichten",
+ "searchResults": "Suchergebnisse für {{ search }}"
},
"post": {
"context": "Kontext",
diff --git a/src/main/webapp/i18n/en/metis.json b/src/main/webapp/i18n/en/metis.json
index bd0cc952c805..9a3ff0977f2b 100644
--- a/src/main/webapp/i18n/en/metis.json
+++ b/src/main/webapp/i18n/en/metis.json
@@ -90,7 +90,9 @@
"TECH_SUPPORT": "Tech Support",
"ORGANIZATION": "Organization",
"RANDOM": "Random",
- "ANNOUNCEMENT": "Announcement"
+ "ANNOUNCEMENT": "Announcement",
+ "allPublicMessages": "All Public Messages",
+ "searchResults": "Search Results for '{{ search }}'"
},
"post": {
"context": "Context",
diff --git a/src/test/javascript/spec/component/overview/course-conversations/course-wide-search.component.spec.ts b/src/test/javascript/spec/component/overview/course-conversations/course-wide-search.component.spec.ts
index 969c54184708..288ed74a8c86 100644
--- a/src/test/javascript/spec/component/overview/course-conversations/course-wide-search.component.spec.ts
+++ b/src/test/javascript/spec/component/overview/course-conversations/course-wide-search.component.spec.ts
@@ -14,11 +14,12 @@ import { BehaviorSubject } from 'rxjs';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { MessageInlineInputComponent } from 'app/shared/metis/message/message-inline-input/message-inline-input.component';
import { PostCreateEditModalComponent } from 'app/shared/metis/posting-create-edit-modal/post-create-edit-modal/post-create-edit-modal.component';
-import { MockComponent, MockPipe, MockProvider } from 'ng-mocks';
+import { MockComponent, MockDirective, MockPipe, MockProvider } from 'ng-mocks';
import { PostSortCriterion, SortDirection } from 'app/shared/metis/metis.util';
import { metisExamChannelDTO, metisExerciseChannelDTO, metisGeneralChannelDTO, metisLectureChannelDTO } from '../../../helpers/sample/metis-sample-data';
import { getElement } from '../../../helpers/utils/general.utils';
import { NgbTooltipMocksModule } from '../../../helpers/mocks/directive/ngbTooltipMocks.module';
+import { TranslateDirective } from 'app/shared/language/translate.directive';
@Directive({
// eslint-disable-next-line @angular-eslint/directive-selector
@@ -68,6 +69,7 @@ describe('CourseWideSearchComponent', () => {
MockComponent(PostingThreadComponent),
MockComponent(MessageInlineInputComponent),
MockComponent(PostCreateEditModalComponent),
+ MockDirective(TranslateDirective),
],
providers: [MockProvider(MetisConversationService), MockProvider(MetisService), MockProvider(NgbModal)],
}).compileComponents();
diff --git a/src/test/javascript/spec/component/overview/course-conversations/dialogs/conversation-detail-dialog/tabs/conversation-members/conversation-member-row/conversation-member-row.component.spec.ts b/src/test/javascript/spec/component/overview/course-conversations/dialogs/conversation-detail-dialog/tabs/conversation-members/conversation-member-row/conversation-member-row.component.spec.ts
index e58af8239cc1..133fc64978b9 100644
--- a/src/test/javascript/spec/component/overview/course-conversations/dialogs/conversation-detail-dialog/tabs/conversation-members/conversation-member-row/conversation-member-row.component.spec.ts
+++ b/src/test/javascript/spec/component/overview/course-conversations/dialogs/conversation-detail-dialog/tabs/conversation-members/conversation-member-row/conversation-member-row.component.spec.ts
@@ -22,6 +22,7 @@ import { of } from 'rxjs';
import { isGroupChatDTO } from 'app/entities/metis/conversation/group-chat.model';
import { By } from '@angular/platform-browser';
import { NgbDropdownMocksModule } from '../../../../../../../../helpers/mocks/directive/ngbDropdownMocks.module';
+import { getElement } from '../../../../../../../../helpers/utils/general.utils';
const memberTemplate = {
id: 1,
@@ -167,6 +168,11 @@ examples.forEach((activeConversation) => {
}
}));
+ it('should display default profile picture', () => {
+ fixture.detectChanges();
+ expect(getElement(fixture.debugElement, '.conversation-member-row-default-profile-picture')).not.toBeNull();
+ });
+
function checkGrantModeratorButton(shouldExist: boolean) {
const grantModeratorRoleButton = fixture.debugElement.query(By.css('.grant-moderator'));
if (shouldExist) {