Skip to content

Commit

Permalink
Merge pull request #211 from dancier/feature/improve-chat
Browse files Browse the repository at this point in the history
improve chat and add new "empty" view for recommendations
  • Loading branch information
halbekanne authored Dec 20, 2023
2 parents 9c7083a + 831395a commit 881beef
Show file tree
Hide file tree
Showing 16 changed files with 363 additions and 253 deletions.
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
File renamed without changes.
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

0 comments on commit 881beef

Please sign in to comment.