diff --git a/angular.json b/angular.json
index 4220595..c32e89a 100644
--- a/angular.json
+++ b/angular.json
@@ -32,7 +32,6 @@
}
],
"styles": [
- "@angular/material/prebuilt-themes/azure-blue.css",
"node_modules/ngx-toastr/toastr.css",
"src/styles.scss"
],
@@ -40,6 +39,12 @@
},
"configurations": {
"production": {
+ "fileReplacements": [
+ {
+ "replace": "src/environment/environment.ts",
+ "with": "src/environment/environment.prod.ts"
+ }
+ ],
"budgets": [
{
"type": "initial",
@@ -93,7 +98,6 @@
}
],
"styles": [
- "@angular/material/prebuilt-themes/cyan-orange.css",
"src/styles.scss"
],
"scripts": []
diff --git a/package.json b/package.json
index d259baf..5e882e2 100644
--- a/package.json
+++ b/package.json
@@ -38,4 +38,4 @@
"karma-jasmine-html-reporter": "~2.1.0",
"typescript": "~5.7.2"
}
-}
+}
\ No newline at end of file
diff --git a/src/app/app.component.html b/src/app/app.component.html
deleted file mode 100644
index 67e7bd4..0000000
--- a/src/app/app.component.html
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/src/app/app.component.scss b/src/app/app.component.scss
deleted file mode 100644
index e69de29..0000000
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index a9c69e6..0a5429b 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -4,9 +4,6 @@ import { RouterOutlet } from '@angular/router';
@Component({
selector: 'app-root',
imports: [RouterOutlet],
- templateUrl: './app.component.html',
- styleUrl: './app.component.scss'
+ template: "",
})
-export class AppComponent {
-
-}
+export class AppComponent { }
diff --git a/src/app/chat/chat.module.ts b/src/app/chat/chat.module.ts
index 4cc1552..c4d22db 100644
--- a/src/app/chat/chat.module.ts
+++ b/src/app/chat/chat.module.ts
@@ -10,6 +10,8 @@ import { MessageComponent } from './chat/feed/message/message.component';
import { ProfilePictureComponent } from './chat/feed/message/profile-picture/profile-picture.component';
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';
@NgModule({
declarations: [
@@ -24,7 +26,9 @@ import { MatBadgeModule } from '@angular/material/badge';
CommonModule,
ChatRoutingModule,
ToolbarComponent,
- MatBadgeModule
+ MatBadgeModule,
+ MatButtonModule,
+ MatTooltipModule
]
})
export class ChatModule { }
diff --git a/src/app/chat/chat/channel-list/channel-entry/channel-entry.component.html b/src/app/chat/chat/channel-list/channel-entry/channel-entry.component.html
index 209483a..3580943 100644
--- a/src/app/chat/chat/channel-list/channel-entry/channel-entry.component.html
+++ b/src/app/chat/chat/channel-list/channel-entry/channel-entry.component.html
@@ -1,7 +1,11 @@
-
-
-
- {{channel.name}}
-
-
+
+
diff --git a/src/app/chat/chat/channel-list/channel-entry/channel-entry.component.scss b/src/app/chat/chat/channel-list/channel-entry/channel-entry.component.scss
index 3bb7cd2..addffd9 100644
--- a/src/app/chat/chat/channel-list/channel-entry/channel-entry.component.scss
+++ b/src/app/chat/chat/channel-list/channel-entry/channel-entry.component.scss
@@ -1,10 +1,5 @@
-div {
- padding: 5px 10px;
- cursor: pointer;
-}
-
-.selected {
- color: var(--mat-sys-on-secondary);
- background: linear-gradient(to right, var(--mat-sys-secondary), var(--mat-sys-secondary-container) 90%, rgba(0,0,0,0));
- transition: all .3s ease-in-out;
+button {
+ width: 70%;
+ display: block;
+ margin: 10px auto;
}
\ No newline at end of file
diff --git a/src/app/chat/chat/channel-list/channel-list.component.scss b/src/app/chat/chat/channel-list/channel-list.component.scss
index e599875..e7a6dca 100644
--- a/src/app/chat/chat/channel-list/channel-list.component.scss
+++ b/src/app/chat/chat/channel-list/channel-list.component.scss
@@ -1,4 +1,3 @@
:host {
- color: var(--mat-sys-on-surface);
- background-color: var(--mat-sys-surface);
+ background-color: var(--mat-sys-surface-container);
}
\ No newline at end of file
diff --git a/src/app/chat/chat/chat.component.scss b/src/app/chat/chat/chat.component.scss
index 650d6ca..d9c4120 100644
--- a/src/app/chat/chat/chat.component.scss
+++ b/src/app/chat/chat/chat.component.scss
@@ -1,5 +1,5 @@
#container {
display: grid;
- grid-template-columns: minmax(auto, 200px) 1fr;
+ grid-template-columns: minmax(auto, 150px) 1fr;
height: 100%;
}
\ No newline at end of file
diff --git a/src/app/chat/chat/feed/feed.component.scss b/src/app/chat/chat/feed/feed.component.scss
index 75740b9..1ce52f5 100644
--- a/src/app/chat/chat/feed/feed.component.scss
+++ b/src/app/chat/chat/feed/feed.component.scss
@@ -1,8 +1,7 @@
:host {
- color: var(--mat-sys-on-background);
- background-color: var(--mat-sys-background);
-
- border-left: 2px solid var(--mat-sys-secondary);
- border-top: 2px solid var(--mat-sys-secondary);
+ border-left: 2px solid var(--mat-sys-surface-variant);
+ border-top: 2px solid var(--mat-sys-surface-variant);
border-radius: 10px 0;
+
+ background-color: var(--mat-sys-background);
}
\ 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 fcfcef8..d86becc 100644
--- a/src/app/chat/chat/feed/feed.component.ts
+++ b/src/app/chat/chat/feed/feed.component.ts
@@ -3,6 +3,7 @@ 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';
@Component({
selector: 'app-feed',
@@ -14,14 +15,20 @@ export class FeedComponent implements OnChanges {
private readonly DEFAULT_CHANNEL_ID: number = 1;
@Input('channel') public channel?: Channel;
- public messages!: Message[];
+ public messages: Message[] = [];
+
+ public subscription?: Subscription;
constructor(private chatService: ChatService, private userService: UserService) { }
ngOnChanges() {
- this.chatService.GetMessages(this.channel?.id ?? this.DEFAULT_CHANNEL_ID)
- .subscribe(messages => {
- this.messages = messages;
- });
+ if (this.subscription) {
+ this.subscription.unsubscribe();
+ this.messages = [];
+ }
+
+ this.subscription =
+ this.chatService.GetMessages(this.channel?.id ?? this.DEFAULT_CHANNEL_ID)
+ .subscribe(message => this.messages.push(message));
}
}
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 2dda265..f743977 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
@@ -3,5 +3,5 @@ img {
width: 100%;
border-radius: 50%;
- box-shadow: 0px 0px 5px rgba(0, 0, 0, 1);
+ box-shadow: 0px 0px 5px var(--mat-sys-on-background);
}
\ No newline at end of file
diff --git a/src/app/chat/chat/feed/message/profile-picture/profile-picture.component.ts b/src/app/chat/chat/feed/message/profile-picture/profile-picture.component.ts
index bc35fd2..8b9752c 100644
--- a/src/app/chat/chat/feed/message/profile-picture/profile-picture.component.ts
+++ b/src/app/chat/chat/feed/message/profile-picture/profile-picture.component.ts
@@ -1,5 +1,6 @@
import { Component, Input, OnInit } from '@angular/core';
import { UserService } from '../../../../../services/user.service';
+import { ToastrService } from 'ngx-toastr';
@Component({
selector: 'app-profile-picture',
@@ -11,10 +12,15 @@ export class ProfilePictureComponent implements OnInit {
@Input("username") public username!: string;
public url?: string;
- constructor(private userService: UserService) { }
+ constructor(private userService: UserService, private toastrService: ToastrService) { }
ngOnInit(): void {
this.userService.GetProfilePictureURL(this.username)
- .subscribe(url => this.url = url);
+ .subscribe({
+ next: url => this.url = url,
+ error: _ => {
+ this.toastrService.error("failed to fetch user info", "Error")
+ }
+ });
}
}
diff --git a/src/app/common/toolbar/toolbar.component.scss b/src/app/common/toolbar/toolbar.component.scss
index b4e2bcb..9d8ba15 100644
--- a/src/app/common/toolbar/toolbar.component.scss
+++ b/src/app/common/toolbar/toolbar.component.scss
@@ -1,3 +1,7 @@
+mat-toolbar {
+ background-color: var(--mat-sys-surface-container);
+}
+
.toolbar-right-side {
width: 100%;
diff --git a/src/app/services/auth.service.ts b/src/app/services/auth.service.ts
index efc42aa..f5bc6ce 100644
--- a/src/app/services/auth.service.ts
+++ b/src/app/services/auth.service.ts
@@ -3,13 +3,12 @@ import { inject, Injectable } from '@angular/core';
import { catchError, map, Observable, of } from 'rxjs';
import { LoginResponse, RegisterResponse } from './responses/auth';
import { ActivatedRouteSnapshot, CanActivateFn, Router, RouterStateSnapshot } from '@angular/router';
+import { environment } from '../../environment/environment';
@Injectable({
providedIn: 'root'
})
export class AuthService {
- private readonly API_BASE: string = "http://localhost:5000"
-
private readonly SESSION_COOKIE: string = "session";
private readonly USERNAME_FIELD: string = "username";
private readonly PASSWORD_FIELD: string = "password";
@@ -18,7 +17,7 @@ export class AuthService {
constructor(private http: HttpClient) { }
public Login(username: string, password: string): Observable
{
- let url = `${this.API_BASE}/auth/login`;
+ let url = `${environment.apiBase}/auth/login`;
let formData = new FormData();
formData.append(this.USERNAME_FIELD, username);
@@ -28,7 +27,7 @@ export class AuthService {
}
public Register(username: string, password: string, repeatPassword: string): Observable {
- let url = `${this.API_BASE}/auth/register`;
+ let url = `${environment.apiBase}/auth/register`;
let formData = new FormData();
formData.append(this.USERNAME_FIELD, username);
@@ -39,7 +38,7 @@ export class AuthService {
}
public Logout(): Observable {
- let url = `${this.API_BASE}/auth/logout`;
+ let url = `${environment.apiBase}/auth/logout`;
return this.http.get(url, { withCredentials: true }).pipe(
map(() => true),
@@ -48,7 +47,7 @@ export class AuthService {
}
public Bump(): Observable {
- let url = `${this.API_BASE}/auth/bump`
+ let url = `${environment.apiBase}/auth/bump`
return this.http.get(url, { withCredentials: true }).pipe(
map(() => true),
@@ -68,8 +67,8 @@ export class AuthService {
}
export const IsLoggedInCanActivate: CanActivateFn = (
- route: ActivatedRouteSnapshot,
- state: RouterStateSnapshot
+ _: ActivatedRouteSnapshot,
+ __: RouterStateSnapshot
) => {
if (inject(AuthService).IsLoggedIn()) {
return true;
diff --git a/src/app/services/chat.service.ts b/src/app/services/chat.service.ts
index e7ded5c..8e82b80 100644
--- a/src/app/services/chat.service.ts
+++ b/src/app/services/chat.service.ts
@@ -1,5 +1,5 @@
import { Injectable } from '@angular/core';
-import { Observable } from 'rxjs';
+import { from, Observable } from 'rxjs';
import { Channel } from '../models/channel';
import { Message } from '../models/message';
@@ -21,7 +21,7 @@ export class ChatService {
},
{
id: 1,
- name: 'gaming',
+ name: 'XIV. Leo',
description: 'this is another channel'
},
]);
@@ -32,25 +32,142 @@ export class ChatService {
// TODO: implement
// TODO: refactor this so it first returns the n last messages,
// then listens for incoming messages and forwards them as they come
- public GetMessages(channelID: number): Observable {
- return new Observable(subscriber => {
- subscriber.next([
- {
- id: 1,
- sender: 'Test User 1',
- channel: 1,
- time: new Date(),
- content: 'this is my first message'
- },
- {
- id: 2,
- sender: 'Test User 2',
- channel: 2,
- time: new Date(),
- content: 'this is my second message'
- }
- ]);
- subscriber.complete();
- });
+ public GetMessages(channelID: number): Observable {
+
+ return from([
+ {
+ 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: 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'
+ }
+ ]);
}
}
diff --git a/src/app/services/responses/user.ts b/src/app/services/responses/user.ts
new file mode 100644
index 0000000..0c5fccb
--- /dev/null
+++ b/src/app/services/responses/user.ts
@@ -0,0 +1,6 @@
+import { User } from "../../models/user";
+import { APIResponse } from "./basic";
+
+export class UserInfoResponse extends APIResponse {
+ public user?: User;
+}
\ No newline at end of file
diff --git a/src/app/services/user.service.ts b/src/app/services/user.service.ts
index 112fe1c..a887b87 100644
--- a/src/app/services/user.service.ts
+++ b/src/app/services/user.service.ts
@@ -1,23 +1,52 @@
import { Injectable } from '@angular/core';
-import { Observable } from 'rxjs';
+import { catchError, map, Observable, throwError } from 'rxjs';
import { User } from '../models/user';
+import { HttpClient } from '@angular/common/http';
+import { UserInfoResponse } from './responses/user';
+import { environment } from '../../environment/environment';
@Injectable({
providedIn: 'root'
})
export class UserService {
- constructor() { }
+ private users: Map = new Map();
- // TODO: implement
- public GetProfilePictureURL(username: string): Observable {
- return new Observable(subscriber => {
- subscriber.next("https://i.pinimg.com/736x/00/70/16/00701602b0eac0390b3107b9e2a665e0.jpg");
- subscriber.complete();
- });
+ constructor(private http: HttpClient) { }
+
+ public GetUser(username: string): Observable {
+ if (this.users.has(username)) {
+ return new Observable(subscriber => {
+ subscriber.next(this.users.get(username)!);
+ subscriber.complete();
+ });
+ } else {
+ let url = `${environment.apiBase}/user/info/${username}`
+ return this.http.get(url, { withCredentials: true }).pipe(
+ map(response => {
+ if (response.error) {
+ throw new Error(response.error);
+ }
+
+ if (response.user) {
+ this.users.set(username, response.user);
+ return response.user;
+ }
+
+ throw new Error("bad API response, missing user with no error");
+ }),
+ catchError(error => throwError(() => new Error(error.error.message)))
+ );
+ }
}
- // TODO: implement
- public GetUser(username: string): Observable {
- throw new Error('Not implemented');
+ public GetProfilePictureURL(username: string): Observable {
+ if (this.users.has(username)) {
+ return new Observable(subscriber => {
+ subscriber.next(this.users.get(username)!.picture);
+ subscriber.complete();
+ });
+ } else {
+ return this.GetUser(username).pipe(map(user => user.picture));
+ }
}
}
diff --git a/src/environment/environment.prod.ts b/src/environment/environment.prod.ts
new file mode 100644
index 0000000..d59ecc4
--- /dev/null
+++ b/src/environment/environment.prod.ts
@@ -0,0 +1,4 @@
+export const environment = {
+ production: true,
+ apiBase: "https://chat.tek.govt.hu/api"
+}
\ No newline at end of file
diff --git a/src/environment/environment.ts b/src/environment/environment.ts
new file mode 100644
index 0000000..a367b01
--- /dev/null
+++ b/src/environment/environment.ts
@@ -0,0 +1,4 @@
+export const environment = {
+ production: false,
+ apiBase: "http://localhost:5000"
+}
\ No newline at end of file
diff --git a/src/index.html b/src/index.html
index 4fb474e..47e6b64 100644
--- a/src/index.html
+++ b/src/index.html
@@ -2,7 +2,7 @@
- Ui
+ Chat
diff --git a/src/styles.scss b/src/styles.scss
index 9862660..ffe6595 100644
--- a/src/styles.scss
+++ b/src/styles.scss
@@ -1,4 +1,24 @@
-/* You can add global styles to this file, and also import other style files */
+@use '@angular/material' as mat;
-html, body { height: 100%; }
-body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
\ No newline at end of file
+html,
+body {
+ height: 100%;
+ margin: 0;
+
+ font-family: var(--mat-sys-body-medium-font);
+ background-color: var(--mat-sys-surface-container);
+
+ color-scheme: light dark;
+ @include mat.theme((
+ color: mat.$azure-palette,
+ typography: Roboto,
+ density: 0));
+}
+
+.force-light-mode {
+ color-scheme: light !important;
+}
+
+.force-dark-mode {
+ color-scheme: dark !important;
+}
\ No newline at end of file