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: Improve sidebar user interface design #9356

Merged
merged 34 commits into from
Oct 19, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
5e0990e
updated sidebar with new buttons, fix channel prefixes, fix empty sec…
asliayk Sep 22, 2024
8a1d9b3
refactor
asliayk Sep 23, 2024
46ec02f
refactor
asliayk Sep 23, 2024
484ad39
refactor
asliayk Sep 23, 2024
8913b9b
Merge branch 'develop' into feature/communication/refactor-sidebar
asliayk Sep 23, 2024
8b55957
added new tests
asliayk Sep 26, 2024
87a0c5c
added new tests
asliayk Sep 26, 2024
a556e90
added new tests
asliayk Sep 26, 2024
d8dde0c
added new tests
asliayk Sep 26, 2024
e6fa965
Update src/main/webapp/app/shared/sidebar/sidebar-card-large/sidebar-…
asliayk Sep 26, 2024
d7c7787
Update src/main/webapp/app/shared/sidebar/sidebar-accordion/sidebar-a…
asliayk Sep 26, 2024
f36fc33
refactor
asliayk Sep 26, 2024
d1b1f5b
Merge remote-tracking branch 'origin/feature/communication/refactor-s…
asliayk Sep 26, 2024
b80e041
refactor
asliayk Sep 26, 2024
9fcb02c
fix color
asliayk Sep 26, 2024
94308dd
refactor
asliayk Oct 1, 2024
5a078b2
updated code according to reviews
asliayk Oct 5, 2024
739c420
Merge branch 'develop' into feature/communication/refactor-sidebar
asliayk Oct 5, 2024
d7b4d1a
used one button instead of two buttons, refactored style
asliayk Oct 5, 2024
40f3913
resolved conflict
asliayk Oct 5, 2024
cf15dfb
Merge branch 'develop' into feature/communication/refactor-sidebar
asliayk Oct 5, 2024
eb2010f
resolved conflict
asliayk Oct 5, 2024
9c80f6d
fixed input deletion bug on users selector component
asliayk Oct 6, 2024
b908c5a
Merge branch 'develop' into feature/communication/refactor-sidebar
asliayk Oct 11, 2024
468c4e8
fix message disabled issue & refactor button shape
asliayk Oct 11, 2024
6eb7168
fix button
asliayk Oct 11, 2024
bf14c71
Merge branch 'develop' into feature/communication/refactor-sidebar
asliayk Oct 14, 2024
256716f
refactor according to reviews
asliayk Oct 14, 2024
3683388
updated tests
asliayk Oct 14, 2024
e0f3f02
updated test
asliayk Oct 15, 2024
cb8cb07
Merge branch 'develop' into feature/communication/refactor-sidebar
asliayk Oct 17, 2024
d040704
Merge branch 'develop' into feature/communication/refactor-sidebar
asliayk Oct 18, 2024
5f5bf5c
fix conflict
asliayk Oct 18, 2024
f6bafc6
Merge branch 'develop' into feature/communication/refactor-sidebar
krusche Oct 19, 2024
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 @@ -29,10 +29,14 @@
[itemSelected]="conversationSelected"
[courseId]="course.id"
[sidebarData]="sidebarData"
(onPlusPressed)="onAccordionPlusButtonPressed($event)"
(onCreateChannelPressed)="onCreateChannelPressed()"
(onBrowsePressed)="onBrowseChannelPressed()"
(onDirectChatPressed)="onCreateDirectChatPressed()"
(onGroupChatPressed)="onCreateGroupChatPressed()"
krusche marked this conversation as resolved.
Show resolved Hide resolved
[showAddOption]="CHANNEL_TYPE_SHOW_ADD_OPTION"
[channelTypeIcon]="CHANNEL_TYPE_ICON"
[collapseState]="DEFAULT_COLLAPSE_STATE"
[inCommunication]="true"
/>
</div>
@if (course && !activeConversation && isCodeOfConductPresented) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import { ConversationDTO } from 'app/entities/metis/conversation/conversation.mo
import { Post } from 'app/entities/metis/post.model';
import { ActivatedRoute, Router } from '@angular/router';
import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
import { EMPTY, Subject, Subscription, from, take, takeUntil } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { EMPTY, Observable, Subject, Subscription, from, take, takeUntil } from 'rxjs';
import { catchError, debounceTime, distinctUntilChanged } from 'rxjs/operators';
import { MetisConversationService } from 'app/shared/metis/metis-conversation.service';
import { ChannelSubType, getAsChannelDTO } from 'app/entities/metis/conversation/channel.model';
import { ChannelDTO, ChannelSubType, getAsChannelDTO } from 'app/entities/metis/conversation/channel.model';
import { MetisService } from 'app/shared/metis/metis.service';
import { Course, isMessagingEnabled } from 'app/entities/course.model';
import { PageType, SortDirection } from 'app/shared/metis/metis.util';
Expand All @@ -16,11 +16,12 @@ import { CourseWideSearchComponent, CourseWideSearchConfig } from 'app/overview/
import { AccordionGroups, ChannelAccordionShowAdd, ChannelTypeIcons, CollapseState, SidebarCardElement, SidebarData } from 'app/types/sidebar';
import { CourseOverviewService } from 'app/overview/course-overview.service';
import { GroupChatCreateDialogComponent } from 'app/overview/course-conversations/dialogs/group-chat-create-dialog/group-chat-create-dialog.component';
import { defaultFirstLayerDialogOptions } from 'app/overview/course-conversations/other/conversation.util';
import { defaultFirstLayerDialogOptions, defaultSecondLayerDialogOptions } from 'app/overview/course-conversations/other/conversation.util';
import { UserPublicInfoDTO } from 'app/core/user/user.model';
import { OneToOneChatCreateDialogComponent } from 'app/overview/course-conversations/dialogs/one-to-one-chat-create-dialog/one-to-one-chat-create-dialog.component';
import { ChannelsOverviewDialogComponent } from 'app/overview/course-conversations/dialogs/channels-overview-dialog/channels-overview-dialog.component';
import { ChannelAction, ChannelsOverviewDialogComponent } from 'app/overview/course-conversations/dialogs/channels-overview-dialog/channels-overview-dialog.component';
import { ProfileService } from 'app/shared/layouts/profiles/profile.service';
import { ChannelsCreateDialogComponent } from 'app/overview/course-conversations/dialogs/channels-create-dialog/channels-create-dialog.component';

const DEFAULT_CHANNEL_GROUPS: AccordionGroups = {
favoriteChannels: { entityData: [] },
Expand Down Expand Up @@ -114,7 +115,9 @@ export class CourseConversationsComponent implements OnInit, OnDestroy {
faFilter = faFilter;
faSearch = faSearch;

// MetisConversationService is created in course overview, so we can use it here
createChannelFn?: (channel: ChannelDTO) => Observable<never>;
krusche marked this conversation as resolved.
Show resolved Hide resolved
channelActions$ = new Subject<ChannelAction>();

asliayk marked this conversation as resolved.
Show resolved Hide resolved
constructor(
private router: Router,
private activatedRoute: ActivatedRoute,
Expand Down Expand Up @@ -162,6 +165,16 @@ export class CourseConversationsComponent implements OnInit, OnDestroy {
this.metisConversationService.checkIsCodeOfConductAccepted(this.course!);
this.isServiceSetUp = true;
}
this.channelActions$
.pipe(
debounceTime(500),
distinctUntilChanged((prev, curr) => prev.action === curr.action && prev.channel.id === curr.channel.id),
takeUntil(this.ngUnsubscribe),
)
.subscribe((channelAction) => {
this.performChannelAction(channelAction);
});
krusche marked this conversation as resolved.
Show resolved Hide resolved
this.createChannelFn = (channel: ChannelDTO) => this.metisConversationService.createChannel(channel);
});

this.profileSubscription = this.profileService.getProfileInfo()?.subscribe((profileInfo) => {
Expand All @@ -170,6 +183,21 @@ export class CourseConversationsComponent implements OnInit, OnDestroy {
});
}

performChannelAction(channelAction: ChannelAction) {
if (this.createChannelFn) {
this.createChannelFn(channelAction.channel)
.pipe(takeUntil(this.ngUnsubscribe))
.subscribe({
complete: () => {
this.prepareSidebarData();
},
error: (error) => {
console.error('Error creating channel:', error);
},
krusche marked this conversation as resolved.
Show resolved Hide resolved
});
}
}

subscribeToQueryParameter() {
this.activatedRoute.queryParams.pipe(take(1), takeUntil(this.ngUnsubscribe)).subscribe((queryParams) => {
if (queryParams.conversationId) {
Expand Down Expand Up @@ -276,7 +304,6 @@ export class CourseConversationsComponent implements OnInit, OnDestroy {
storageId: 'conversation',
groupedData: this.accordionConversationGroups,
ungroupedData: this.sidebarConversations,
showAccordionAddOption: true,
showAccordionLeadingIcon: true,
};
}
Expand All @@ -290,14 +317,20 @@ export class CourseConversationsComponent implements OnInit, OnDestroy {
this.courseOverviewService.setSidebarCollapseState('conversation', this.isCollapsed);
}

onAccordionPlusButtonPressed(chatType: string) {
if (chatType === 'groupChats') {
this.openCreateGroupChatDialog();
} else if (chatType === 'directMessages') {
this.openCreateOneToOneChatDialog();
} else {
this.openChannelOverviewDialog(chatType);
}
onCreateChannelPressed() {
this.openCreateChannelDialog();
}

onCreateGroupChatPressed() {
this.openCreateGroupChatDialog();
}

onCreateDirectChatPressed() {
this.openCreateOneToOneChatDialog();
}

onBrowseChannelPressed() {
this.openChannelOverviewDialog();
}

openCreateGroupChatDialog() {
Expand Down Expand Up @@ -338,8 +371,22 @@ export class CourseConversationsComponent implements OnInit, OnDestroy {
});
}

openChannelOverviewDialog(groupKey: string) {
const subType = this.getChannelSubType(groupKey);
openCreateChannelDialog() {
const modalRef: NgbModalRef = this.modalService.open(ChannelsCreateDialogComponent, defaultSecondLayerDialogOptions);
modalRef.componentInstance.course = this.course;
modalRef.componentInstance.initialize();
from(modalRef.result)
.pipe(
catchError(() => EMPTY),
takeUntil(this.ngUnsubscribe),
)
.subscribe((channel: ChannelDTO) => {
this.channelActions$.next({ action: 'create', channel });
krusche marked this conversation as resolved.
Show resolved Hide resolved
});
krusche marked this conversation as resolved.
Show resolved Hide resolved
}

openChannelOverviewDialog() {
const subType = null;
const modalRef: NgbModalRef = this.modalService.open(ChannelsOverviewDialogComponent, defaultFirstLayerDialogOptions);
modalRef.componentInstance.course = this.course;
modalRef.componentInstance.createChannelFn = subType === ChannelSubType.GENERAL ? this.metisConversationService.createChannel : undefined;
Expand Down Expand Up @@ -369,22 +416,6 @@ export class CourseConversationsComponent implements OnInit, OnDestroy {
});
}

getChannelSubType(groupKey: string) {
if (groupKey === 'exerciseChannels') {
return ChannelSubType.EXERCISE;
}
if (groupKey === 'generalChannels') {
return ChannelSubType.GENERAL;
}
if (groupKey === 'lectureChannels') {
return ChannelSubType.LECTURE;
}
if (groupKey === 'examChannels') {
return ChannelSubType.EXAM;
}
return ChannelSubType.GENERAL;
}

toggleChannelSearch() {
this.channelSearchCollapsed = !this.channelSearchCollapsed;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<div class="channels-overview">
<div class="modal-header">
<h4 class="modal-title">
<span>{{ 'artemisApp.dialogs.channelOverview.title.' + channelSubType | artemisTranslate: { courseTitle: course.title } }}</span>
<span>{{ 'artemisApp.dialogs.channelOverview.title' | artemisTranslate: { courseTitle: course.title } }}</span>
asliayk marked this conversation as resolved.
Show resolved Hide resolved
</h4>
<button type="button" class="btn-close" (click)="clear()"></button>
</div>
Expand All @@ -22,36 +22,6 @@ <h4 class="modal-title">
</div>
</div>
}
@if (otherChannels && otherChannels.length > 0) {
<div class="form-group mt-4">
<div (click)="otherChannelsAreCollapsed = !otherChannelsAreCollapsed" class="other-channels">
<fa-icon [icon]="faChevronRight" [rotate]="!otherChannelsAreCollapsed ? 90 : undefined" />
<span class="h5" jhiTranslate="artemisApp.dialogs.channelOverview.otherChannels"></span>
</div>
<div [(ngbCollapse)]="otherChannelsAreCollapsed">
<div class="table-wrapper-scroll-y scrollbar mt-2">
<ul class="list-group">
@for (channel of otherChannels; track otherChannels.indexOf(channel)) {
<li [id]="'channel-' + channel.id" class="list-group-item">
<jhi-channel-item [channel]="channel" (channelAction)="onChannelAction($event)" />
</li>
}
</ul>
</div>
</div>
</div>
}
</div>
<div class="modal-footer justify-content-between">
@if (createChannelFn && canCreateChannel(course)) {
<button
type="button"
class="btn btn-secondary"
(click)="openCreateChannelDialog($event)"
id="createChannel"
jhiTranslate="artemisApp.dialogs.channelOverview.createChannelButton"
></button>
}
</div>
</div>
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { EMPTY, Observable, Subject, debounceTime, distinctUntilChanged, finalize, from, map, takeUntil } from 'rxjs';
import { Observable, Subject, debounceTime, distinctUntilChanged, finalize, map, takeUntil } from 'rxjs';
krusche marked this conversation as resolved.
Show resolved Hide resolved
import { faChevronRight } from '@fortawesome/free-solid-svg-icons';

import { HttpErrorResponse, HttpResponse } from '@angular/common/http';
import { onError } from 'app/shared/util/global.utils';
import { AlertService } from 'app/core/util/alert.service';
import { NgbActiveModal, NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { ChannelService } from 'app/shared/metis/conversations/channel.service';
import { ChannelDTO, ChannelSubType } from 'app/entities/metis/conversation/channel.model';
import { Course } from 'app/entities/course.model';
import { ChannelsCreateDialogComponent } from 'app/overview/course-conversations/dialogs/channels-create-dialog/channels-create-dialog.component';
import { canCreateChannel } from 'app/shared/metis/conversations/conversation-permissions.utils';
import { AbstractDialogComponent } from 'app/overview/course-conversations/dialogs/abstract-dialog.component';
import { defaultSecondLayerDialogOptions } from 'app/overview/course-conversations/other/conversation.util';
import { catchError } from 'rxjs/operators';

export type ChannelActionType = 'register' | 'deregister' | 'view' | 'create';
export type ChannelAction = {
Expand Down Expand Up @@ -44,12 +40,10 @@ export class ChannelsOverviewDialogComponent extends AbstractDialogComponent imp
channelModificationPerformed = false;
isLoading = false;
channels: ChannelDTO[] = [];
otherChannels: ChannelDTO[] = [];

isInitialized = false;

faChevronRight = faChevronRight;
otherChannelsAreCollapsed = true;

initialize() {
super.initialize(['course', 'channelSubType']);
Expand All @@ -61,7 +55,6 @@ export class ChannelsOverviewDialogComponent extends AbstractDialogComponent imp
constructor(
private channelService: ChannelService,
private alertService: AlertService,
private modalService: NgbModal,

activeModal: NgbActiveModal,
) {
Expand Down Expand Up @@ -114,18 +107,6 @@ export class ChannelsOverviewDialogComponent extends AbstractDialogComponent imp
case 'view':
this.close([channelAction.channel, this.channelModificationPerformed]);
break;
case 'create':
if (this.createChannelFn) {
this.createChannelFn(channelAction.channel)
.pipe(takeUntil(this.ngUnsubscribe))
.subscribe({
complete: () => {
this.loadChannelsOfCourse();
this.channelModificationPerformed = true;
},
});
}
break;
}
}

Expand All @@ -142,28 +123,12 @@ export class ChannelsOverviewDialogComponent extends AbstractDialogComponent imp
)
.subscribe({
next: (channels: ChannelDTO[]) => {
this.channels = channels?.filter((channel) => channel.subType === this.channelSubType) ?? [];
this.otherChannels = channels?.filter((channel) => channel.subType !== this.channelSubType) ?? [];
this.channels = channels;
this.noOfChannels = this.channels.length;
krusche marked this conversation as resolved.
Show resolved Hide resolved
},
error: (errorResponse: HttpErrorResponse) => {
onError(this.alertService, errorResponse);
},
});
}

openCreateChannelDialog(event: MouseEvent) {
event.stopPropagation();
const modalRef: NgbModalRef = this.modalService.open(ChannelsCreateDialogComponent, defaultSecondLayerDialogOptions);
modalRef.componentInstance.course = this.course;
modalRef.componentInstance.initialize();
from(modalRef.result)
.pipe(
catchError(() => EMPTY),
takeUntil(this.ngUnsubscribe),
)
.subscribe((channel: ChannelDTO) => {
this.channelActions$.next({ action: 'create', channel });
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
(delete)="deleteChannel()"
[dialogError]="dialogError$"
>
<fa-icon [icon]="faTimes" />
<fa-icon [icon]="faTrash" />
</button>
</div>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { onError } from 'app/shared/util/global.utils';
import { EMPTY, Subject, from, takeUntil } from 'rxjs';
import { HttpErrorResponse } from '@angular/common/http';
import { AlertService } from 'app/core/util/alert.service';
import { faTimes } from '@fortawesome/free-solid-svg-icons';
import { faTrash } from '@fortawesome/free-solid-svg-icons';
asliayk marked this conversation as resolved.
Show resolved Hide resolved
import { canChangeChannelArchivalState, canDeleteChannel, canLeaveConversation } from 'app/shared/metis/conversations/conversation-permissions.utils';
import { GroupChatService } from 'app/shared/metis/conversations/group-chat.service';
import { isGroupChatDTO } from 'app/entities/metis/conversation/group-chat.model';
Expand Down Expand Up @@ -42,7 +42,7 @@ export class ConversationSettingsComponent implements OnInit, OnDestroy {
private dialogErrorSource = new Subject<string>();
dialogError$ = this.dialogErrorSource.asObservable();

faTimes = faTimes;
readonly faTrash = faTrash;

conversationAsChannel: ChannelDTO | undefined;
canLeaveConversation: boolean;
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

Loading
Loading