From 757e5933735d143f52a2dcecd300708322e8ebc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?BENEDEK=20L=C3=A1szl=C3=B3?= Date: Fri, 6 Jun 2025 16:17:10 +0200 Subject: [PATCH] messages --- angular.json | 3 +- proxy.conf.json | 8 + src/app/auth/auth.module.scss | 3 +- src/app/chat/chat.module.ts | 4 +- src/app/chat/chat/feed/feed.component.scss | 2 + src/app/chat/chat/feed/feed.component.ts | 5 +- .../chat/feed/message/message.component.html | 4 +- .../profile-picture.component.html | 3 +- .../profile-picture.component.scss | 20 ++- src/app/models/message.ts | 6 +- src/app/services/auth.service.ts | 35 ++-- src/app/services/chat.service.ts | 164 +++--------------- src/app/services/responses/chat.ts | 5 + src/environment/environment.ts | 2 +- 14 files changed, 98 insertions(+), 166 deletions(-) create mode 100644 proxy.conf.json diff --git a/angular.json b/angular.json index c32e89a..49a4cc9 100644 --- a/angular.json +++ b/angular.json @@ -74,7 +74,8 @@ "buildTarget": "ui:build:production" }, "development": { - "buildTarget": "ui:build:development" + "buildTarget": "ui:build:development", + "proxyConfig": "proxy.conf.json" } }, "defaultConfiguration": "development" diff --git a/proxy.conf.json b/proxy.conf.json new file mode 100644 index 0000000..09967c5 --- /dev/null +++ b/proxy.conf.json @@ -0,0 +1,8 @@ +{ + "/api": { + "target": "http://localhost:5000", + "secure": false, + "ws": true, + "changeOrigin": true + } +} \ No newline at end of file diff --git a/src/app/auth/auth.module.scss b/src/app/auth/auth.module.scss index df7535b..892c32a 100644 --- a/src/app/auth/auth.module.scss +++ b/src/app/auth/auth.module.scss @@ -13,8 +13,7 @@ button { display: block; + margin-bottom: 10px 0; } } - - } \ No newline at end of file diff --git a/src/app/chat/chat.module.ts b/src/app/chat/chat.module.ts index c4d22db..d85d4a2 100644 --- a/src/app/chat/chat.module.ts +++ b/src/app/chat/chat.module.ts @@ -12,6 +12,7 @@ import { ToolbarComponent } from '../common/toolbar/toolbar.component'; import { MatBadgeModule } from '@angular/material/badge'; import { MatButtonModule } from '@angular/material/button'; import { MatTooltipModule } from '@angular/material/tooltip'; +import { MatIconModule } from '@angular/material/icon'; @NgModule({ declarations: [ @@ -28,7 +29,8 @@ import { MatTooltipModule } from '@angular/material/tooltip'; ToolbarComponent, MatBadgeModule, MatButtonModule, - MatTooltipModule + MatTooltipModule, + MatIconModule ] }) export class ChatModule { } diff --git a/src/app/chat/chat/feed/feed.component.scss b/src/app/chat/chat/feed/feed.component.scss index 1ce52f5..ef17fef 100644 --- a/src/app/chat/chat/feed/feed.component.scss +++ b/src/app/chat/chat/feed/feed.component.scss @@ -4,4 +4,6 @@ border-radius: 10px 0; background-color: var(--mat-sys-background); + + overflow-y: scroll; } \ No newline at end of file diff --git a/src/app/chat/chat/feed/feed.component.ts b/src/app/chat/chat/feed/feed.component.ts index 942ad30..8731944 100644 --- a/src/app/chat/chat/feed/feed.component.ts +++ b/src/app/chat/chat/feed/feed.component.ts @@ -1,7 +1,6 @@ -import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; +import { Component, Input, OnChanges, OnDestroy } from '@angular/core'; import { Message } from '../../../models/message'; import { ChatService } from '../../../services/chat.service'; -import { UserService } from '../../../services/user.service'; import { Channel } from '../../../models/channel'; import { Subscription } from 'rxjs'; @@ -21,7 +20,7 @@ export class FeedComponent implements OnChanges, OnDestroy { constructor(private chatService: ChatService) { } - ngOnChanges() { + ngOnChanges(): void { if (this.subscription) { this.subscription.unsubscribe(); this.messages = []; diff --git a/src/app/chat/chat/feed/message/message.component.html b/src/app/chat/chat/feed/message/message.component.html index 8e34014..8d3a5db 100644 --- a/src/app/chat/chat/feed/message/message.component.html +++ b/src/app/chat/chat/feed/message/message.component.html @@ -1,8 +1,8 @@
- +
-
{{this.message.sender}}
+
{{this.message.sender_name}}
{{this.message.content}}
diff --git a/src/app/chat/chat/feed/message/profile-picture/profile-picture.component.html b/src/app/chat/chat/feed/message/profile-picture/profile-picture.component.html index 08dfcb4..662656d 100644 --- a/src/app/chat/chat/feed/message/profile-picture/profile-picture.component.html +++ b/src/app/chat/chat/feed/message/profile-picture/profile-picture.component.html @@ -1 +1,2 @@ - \ No newline at end of file + +face \ No newline at end of file diff --git a/src/app/chat/chat/feed/message/profile-picture/profile-picture.component.scss b/src/app/chat/chat/feed/message/profile-picture/profile-picture.component.scss index f743977..86a1299 100644 --- a/src/app/chat/chat/feed/message/profile-picture/profile-picture.component.scss +++ b/src/app/chat/chat/feed/message/profile-picture/profile-picture.component.scss @@ -1,7 +1,23 @@ -img { +:host { display: inline-block; width: 100%; + aspect-ratio: 1; border-radius: 50%; - box-shadow: 0px 0px 5px var(--mat-sys-on-background); + box-shadow: 0px 0px 2px var(--mat-sys-on-background); +} + +// style alt for missing images +img { + display: flex; + justify-content: center; + padding-top: 15px; + overflow: hidden; +} + +mat-icon { + width: 100% !important; + height: 100% !important; + text-align: center; + font-size: 50px; } \ No newline at end of file diff --git a/src/app/models/message.ts b/src/app/models/message.ts index 6644397..ed3be1c 100644 --- a/src/app/models/message.ts +++ b/src/app/models/message.ts @@ -1,9 +1,7 @@ -import { Timestamp } from "rxjs" - export class Message { public id!: number - public sender!: string - public channel!: number + public sender_name!: string + public channel_id!: number public time!: Date public content!: string } \ No newline at end of file diff --git a/src/app/services/auth.service.ts b/src/app/services/auth.service.ts index f5bc6ce..d6a4b6c 100644 --- a/src/app/services/auth.service.ts +++ b/src/app/services/auth.service.ts @@ -1,6 +1,6 @@ import { HttpClient } from '@angular/common/http'; import { inject, Injectable } from '@angular/core'; -import { catchError, map, Observable, of } from 'rxjs'; +import { catchError, map, Observable, of, tap } from 'rxjs'; import { LoginResponse, RegisterResponse } from './responses/auth'; import { ActivatedRouteSnapshot, CanActivateFn, Router, RouterStateSnapshot } from '@angular/router'; import { environment } from '../../environment/environment'; @@ -14,6 +14,8 @@ export class AuthService { private readonly PASSWORD_FIELD: string = "password"; private readonly REPEAT_PASSWORD_FIELD: string = "repeatPassword"; + private loggedIn: boolean = false; + constructor(private http: HttpClient) { } public Login(username: string, password: string): Observable { @@ -51,29 +53,40 @@ export class AuthService { return this.http.get(url, { withCredentials: true }).pipe( map(() => true), - catchError(() => of(false)) + catchError(() => of(false)), + tap(result => this.loggedIn = result) ); } - public IsLoggedIn(): boolean { + public HasToken(): boolean { let cookies = document.cookie.split(';'); let found = false; cookies.forEach(cookie => { cookie = cookie.trim(); found = found || cookie.startsWith(this.SESSION_COOKIE + "=") && cookie.split('=', 2)[1] != ''; }); + return found; } + + public IsLoggedIn(): boolean { return this.loggedIn; } } export const IsLoggedInCanActivate: CanActivateFn = ( _: ActivatedRouteSnapshot, __: RouterStateSnapshot -) => { - if (inject(AuthService).IsLoggedIn()) { - return true; - } else { - inject(Router).navigateByUrl("auth/login"); - return false; - } -} \ No newline at end of file +): Observable => { + const authService = inject(AuthService); + const router = inject(Router); + + return authService.Bump().pipe( + map(isValid => { + if (isValid) { + return true; + } else { + router.navigate(['auth/login']); + return false; + } + }) + ); +}; \ No newline at end of file diff --git a/src/app/services/chat.service.ts b/src/app/services/chat.service.ts index 6a37247..de15f61 100644 --- a/src/app/services/chat.service.ts +++ b/src/app/services/chat.service.ts @@ -1,10 +1,11 @@ import { Injectable } from '@angular/core'; -import { catchError, from, map, Observable, throwError } from 'rxjs'; +import { catchError, from, map, merge, mergeMap, Observable, throwError } from 'rxjs'; +import { webSocket } from 'rxjs/webSocket'; import { Channel } from '../models/channel'; import { Message } from '../models/message'; import { HttpClient } from '@angular/common/http'; import { environment } from '../../environment/environment'; -import { ListAvailableChannelsResponse } from './responses/chat'; +import { GetMessagesResponse, ListAvailableChannelsResponse } from './responses/chat'; @Injectable({ providedIn: 'root' @@ -32,143 +33,30 @@ export class ChatService { ); } - // TODO: implement public GetMessages(channelID: number): Observable { + let url = `${environment.apiBase}/chat/messages/${channelID}`; + let messages: Observable = + this.http.get(url, { withCredentials: true }) + .pipe( + map(response => { + if (response.error) { + throw new Error(response.error); + } - return from([ - { - id: 1, - sender: 'admin', - channel: 1, - time: new Date(), - content: 'this is my first message' - }, - { - id: 1, - sender: 'alice', - channel: 1, - time: new Date(), - content: 'this is my first message' - }, - { - id: 1, - sender: 'bob', - channel: 1, - time: new Date(), - content: 'this is my first message' - }, - { - id: 1, - sender: 'charlie', - channel: 1, - time: new Date(), - content: 'this is my first message' - }, - { - id: 1, - sender: 'admin', - channel: 1, - time: new Date(), - content: 'this is my first message' - }, - { - id: 1, - sender: 'admin', - channel: 1, - time: new Date(), - content: 'this is my first message' - }, - { - id: 1, - sender: 'admin', - channel: 1, - time: new Date(), - content: 'this is my first message' - }, - { - id: 1, - sender: 'admin', - channel: 1, - time: new Date(), - content: 'this is my first message' - }, - { - id: 1, - sender: 'admin', - channel: 1, - time: new Date(), - content: 'this is my first message' - }, - { - id: 1, - sender: 'admin', - channel: 1, - time: new Date(), - content: 'this is my first message' - }, - { - id: 1, - sender: 'admin', - channel: 1, - time: new Date(), - content: 'this is my first message' - }, - { - id: 1, - sender: 'admin', - channel: 1, - time: new Date(), - content: 'this is my first message' - }, - { - id: 1, - sender: 'admin', - channel: 1, - time: new Date(), - content: 'this is my first message' - }, - { - id: 1, - sender: 'admin', - channel: 1, - time: new Date(), - content: 'this is my first message' - }, - { - id: 1, - sender: 'admin', - channel: 1, - time: new Date(), - content: 'this is my first message' - }, - { - id: 1, - sender: 'admin', - channel: 1, - time: new Date(), - content: 'this is my first message' - }, - { - id: 1, - sender: 'admin', - channel: 1, - time: new Date(), - content: 'this is my first message' - }, - { - id: 1, - sender: 'admin', - channel: 1, - time: new Date(), - content: 'this is my first message' - }, - { - id: 2, - sender: 'admin', - channel: 2, - time: new Date(), - content: 'this is my second message' - } - ]); + if (response.messages) { + return response.messages; + } + + throw new Error("bad API response, missing messages with no error"); + }), + catchError(error => throwError(() => new Error(error.error.message))) + ); + + url = `${environment.apiBase}/chat/subscribe/${channelID}`; + let socket = webSocket(url); + + return merge( + messages.pipe(mergeMap(msgArray => from(msgArray))), + socket.asObservable()); } } diff --git a/src/app/services/responses/chat.ts b/src/app/services/responses/chat.ts index f980204..24923f6 100644 --- a/src/app/services/responses/chat.ts +++ b/src/app/services/responses/chat.ts @@ -1,6 +1,11 @@ import { Channel } from "../../models/channel"; +import { Message } from "../../models/message"; import { APIResponse } from "./basic"; export class ListAvailableChannelsResponse extends APIResponse { public channels?: Channel[]; +} + +export class GetMessagesResponse extends APIResponse { + public messages?: Message[]; } \ No newline at end of file diff --git a/src/environment/environment.ts b/src/environment/environment.ts index a367b01..3d9c8e8 100644 --- a/src/environment/environment.ts +++ b/src/environment/environment.ts @@ -1,4 +1,4 @@ export const environment = { production: false, - apiBase: "http://localhost:5000" + apiBase: "http://localhost:5000/api" } \ No newline at end of file