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