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 (!courseWideSearchConfig.searchTerm) { - All Messages + } @else { - Search Results for "{{ courseWideSearchConfig.searchTerm }}" + }

diff --git a/src/main/webapp/app/overview/course-conversations/dialogs/conversation-detail-dialog/tabs/conversation-members/conversation-member-row/conversation-member-row.component.html b/src/main/webapp/app/overview/course-conversations/dialogs/conversation-detail-dialog/tabs/conversation-members/conversation-member-row/conversation-member-row.component.html index a481df162d51..47eff1257e65 100644 --- a/src/main/webapp/app/overview/course-conversations/dialogs/conversation-detail-dialog/tabs/conversation-members/conversation-member-row/conversation-member-row.component.html +++ b/src/main/webapp/app/overview/course-conversations/dialogs/conversation-detail-dialog/tabs/conversation-members/conversation-member-row/conversation-member-row.component.html @@ -1,11 +1,23 @@ @if (activeConversation && course) {
- + @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) {