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

improve chat and add new "empty" view for recommendations #211

Merged
merged 2 commits into from
Dec 20, 2023
Merged
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
10 changes: 5 additions & 5 deletions src/app/chat/chat-feature.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MountConfig } from 'cypress/angular';
import { CommonModule } from '@angular/common';
import { chats, profilePictures } from '@cypress-support/mock-backend';
import { ChatPageComponent } from './feature/chat-page/chat-page.component';
import { ChatComponent } from './chat.component';

// const timerMock = new TimerMockService();

Expand All @@ -20,19 +20,19 @@ import { ChatPageComponent } from './feature/chat-page/chat-page.component';
// ],
// };

const defaultMountConfig: MountConfig<ChatPageComponent> = {
const defaultMountConfig: MountConfig<ChatComponent> = {
imports: [
CommonModule,
BrowserAnimationsModule,
HttpClientModule,
RouterTestingModule.withRoutes([
{
path: 'chat',
component: ChatPageComponent,
component: ChatComponent,
},
{
path: 'chat/:participantId',
component: ChatPageComponent,
component: ChatComponent,
},
]),
],
Expand Down Expand Up @@ -265,7 +265,7 @@ describe('The chat page', () => {
])
);

cy.mount(ChatPageComponent, defaultMountConfig);
cy.mount(ChatComponent, defaultMountConfig);
cy.contains('Adam Ant').click();
cy.contains('Hello, how are you?').should('be.visible');
cy.contains('I am fine, thanks.').should('be.visible');
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
import {
ChangeDetectionStrategy,
Component,
inject,
Signal,
} from '@angular/core';

import { RouterLink } from '@angular/router';
import { AlertComponent } from '@shared/ui/alert/alert.component';
import { ChatMessageComposerComponent } from '../../ui/message-composer/chat-message-composer.component';
import { ChatMessagesComponent } from '../../ui/chat-messages/chat-messages.component';
import { ChatConversationListComponent } from '../../ui/conversation-list/chat-conversation-list.component';
import { ChatMessageComposerComponent } from './ui/message-composer/chat-message-composer.component';
import { ChatMessagesComponent } from './ui/chat-messages/chat-messages.component';
import { ChatConversationListComponent } from './ui/conversation-list/chat-conversation-list.component';
import { AsyncPipe, NgFor, NgIf } from '@angular/common';
import { ChatStateService } from '../../data-access/chat-state.service';
import { ChatConversationHeaderComponent } from '../../ui/chat-conversation-header.component';
import { ChatStateService } from './data-access/chat-state.service';
import { ChatConversationHeaderComponent } from './ui/chat-conversation-header.component';
import { interval, take } from 'rxjs';

@Component({
Expand All @@ -22,20 +27,27 @@ import { interval, take } from 'rxjs';
>
<div class="flex h-[600px] border">
<app-chat-conversation-list
class="min-w-[300px] overflow-y-auto border-r border-gray-300 py-2 max-md:flex-1 md:flex-none"
class="min-w-[300px] overflow-y-auto overflow-x-hidden border-r border-gray-300 py-2 max-md:flex-1 md:w-[300px] md:flex-none"
[class.max-md:hidden]="chatState.activeChatId() !== null"
></app-chat-conversation-list>
<div
class="flex w-full flex-col bg-gray-100"
[class.max-md:hidden]="chatState.activeChatId() === null"
>
<!-- TODO: für mobile oben einen header ins element legen -->
<!-- TODO: allgemein das ding mit dem chat state nutzbar machen -->
<app-chat-conversation-header></app-chat-conversation-header>
<app-chat-messages class="grow"></app-chat-messages>
<app-chat-message-composer
class="flex-none"
></app-chat-message-composer>
<ng-container *ngIf="hasActiveChat(); else noActiveChat">
<app-chat-messages class="grow"></app-chat-messages>
<app-chat-message-composer
class="flex-none"
></app-chat-message-composer>
</ng-container>
<ng-template #noActiveChat>
<div class="flex h-full flex-col items-center justify-center">
<p class="font-lg text-gray-500">
Wähle einen Chat aus der Liste aus.
</p>
</div>
</ng-template>
</div>
</div>
</ng-container>
Expand All @@ -61,9 +73,9 @@ import { interval, take } from 'rxjs';

<ng-container *ngIf="chatState.chatsFetchState() === 'error'">
<app-alert alertType="error" icon="error">
<p>
<span>
Es ist ein Fehler aufgetreten. Bitte versuchen Sie es später erneut.
</p>
</span>
</app-alert>
</ng-container>

Expand All @@ -89,7 +101,7 @@ import { interval, take } from 'rxjs';
</div>
</ng-container>
</div>`,
styleUrls: ['./chat-page.component.scss'],
styleUrls: ['./chat.component.scss'],
providers: [ChatStateService],
changeDetection: ChangeDetectionStrategy.Default,
standalone: true,
Expand All @@ -105,7 +117,8 @@ import { interval, take } from 'rxjs';
ChatConversationHeaderComponent,
],
})
export class ChatPageComponent {
export class ChatComponent {
chatState = inject(ChatStateService);
delayLoading$ = interval(100).pipe(take(1));
hasActiveChat: Signal<boolean> = this.chatState.hasActiveChat;
}
6 changes: 3 additions & 3 deletions src/app/chat/chat.routes.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { Routes } from '@angular/router';
import { ChatPageComponent } from './feature/chat-page/chat-page.component';
import { ChatComponent } from './chat.component';

export const CHAT_ROUTES: Routes = [
{
path: ':participantId',
component: ChatPageComponent,
component: ChatComponent,
},
{
path: '',
component: ChatPageComponent,
component: ChatComponent,
},
];
12 changes: 5 additions & 7 deletions src/app/chat/data-access/chat-http.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {
ChatDto,
ChatList,
ChatsAndDancers,
CreateChatResponse,
CreateMessageRequest,
DancerId,
DancerMapDto,
Expand Down Expand Up @@ -169,16 +168,15 @@ export class ChatHttpService {
return Array.from(dancerIds.keys());
}

createChat$(participantId: string): Observable<CreateChatResponse> {
/** returns the chat id */
createChat$(participantId: string): Observable<string> {
const body = {
participantIds: [this.profileService.getProfile()?.id, participantId],
};

return this.http.post<CreateChatResponse>(
`${this.chatApiUrl}`,
body,
this.defaultOptions
);
return this.http
.post<{ id: string }>(`${this.chatApiUrl}`, body)
.pipe(map((res) => res.id));
}

sendMessage$(chatId: string, message: string): Observable<void> {
Expand Down
13 changes: 5 additions & 8 deletions src/app/chat/data-access/chat-state.adapter.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import { ChatAdaptState } from './chat-state.service';
import { createAdapter } from '@state-adapt/core';
import {
ChatDto,
CreateChatResponse,
DancerMapDto,
MessagesWithChatId,
} from './chat.types';
import { ChatDto, DancerMapDto, MessagesWithChatId } from './chat.types';
import { HttpErrorResponse } from '@angular/common/http';

export const chatStateAdapter = createAdapter<ChatAdaptState>()({
chatsFetched: (state, chatsDto: ChatDto[]) => {
const newChats = chatsDto
.reverse() // latest created chat first
.filter(
(chatDto) =>
!state.chats.find((stateChat) => stateChat.id === chatDto.chatId)
Expand Down Expand Up @@ -87,10 +83,10 @@ export const chatStateAdapter = createAdapter<ChatAdaptState>()({
openChatWithParticipantId: null,
}),

chatCreated: (state, chat: CreateChatResponse) => ({
chatCreated: (state, chatId: string) => ({
// select new chat
...state,
activeChatId: chat.chatId,
activeChatId: chatId,
chatCreated: true,
}),

Expand Down Expand Up @@ -120,6 +116,7 @@ export const chatStateAdapter = createAdapter<ChatAdaptState>()({
.flat()
.filter((participant) => participant.dancerName === undefined),
activeChatId: (state) => state.activeChatId,
hasActiveChat: (state) => state.activeChatId !== null,
messagesForActiveChat: (state) =>
state.chats.find((chat) => chat.id === state.activeChatId)?.messages ??
[],
Expand Down
3 changes: 3 additions & 0 deletions src/app/chat/data-access/chat-state.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,9 @@ export class ChatStateService {
activeChatParticipants = toSignal(this.chatStore.activeChatParticipants$, {
requireSync: true,
});
hasActiveChat = toSignal(this.chatStore.hasActiveChat$, {
requireSync: true,
});

constructor() {}
}
15 changes: 0 additions & 15 deletions src/app/chat/data-access/chat.types.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
import { Profile } from '../../profile/data-access/types/profile.types';

export type Conversation = {
chatId: string;
participants: ChatParticipant[];
};

export type ChatDto = {
chatId: string;
participantIds: DancerId[];
Expand All @@ -21,8 +14,6 @@ export type ChatMessage = {
createdAt: string;
};

export type ChatType = 'GROUP' | 'DIRECT';

export type ChatList = {
chats: ChatDto[];
};
Expand Down Expand Up @@ -64,12 +55,6 @@ export type CreateMessageRequest = {
text: string;
};

export type ChatData = {
chats: ChatDto[];
dancers: DancerMapDto;
profile: Profile;
};

export type CreateChatResponse = {
chatId: string;
dancerIds: string[];
Expand Down
49 changes: 0 additions & 49 deletions src/app/chat/feature/chat-page/chat-page-demo.component.ts

This file was deleted.

53 changes: 0 additions & 53 deletions src/app/chat/feature/chat-page/chat-service-demo.service.ts

This file was deleted.

Loading
Loading