messages
This commit is contained in:
parent
543dc2b9fb
commit
757e593373
@ -74,7 +74,8 @@
|
|||||||
"buildTarget": "ui:build:production"
|
"buildTarget": "ui:build:production"
|
||||||
},
|
},
|
||||||
"development": {
|
"development": {
|
||||||
"buildTarget": "ui:build:development"
|
"buildTarget": "ui:build:development",
|
||||||
|
"proxyConfig": "proxy.conf.json"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"defaultConfiguration": "development"
|
"defaultConfiguration": "development"
|
||||||
|
8
proxy.conf.json
Normal file
8
proxy.conf.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"/api": {
|
||||||
|
"target": "http://localhost:5000",
|
||||||
|
"secure": false,
|
||||||
|
"ws": true,
|
||||||
|
"changeOrigin": true
|
||||||
|
}
|
||||||
|
}
|
@ -13,8 +13,7 @@
|
|||||||
|
|
||||||
button {
|
button {
|
||||||
display: block;
|
display: block;
|
||||||
|
margin-bottom: 10px 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -12,6 +12,7 @@ import { ToolbarComponent } from '../common/toolbar/toolbar.component';
|
|||||||
import { MatBadgeModule } from '@angular/material/badge';
|
import { MatBadgeModule } from '@angular/material/badge';
|
||||||
import { MatButtonModule } from '@angular/material/button';
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
@ -28,7 +29,8 @@ import { MatTooltipModule } from '@angular/material/tooltip';
|
|||||||
ToolbarComponent,
|
ToolbarComponent,
|
||||||
MatBadgeModule,
|
MatBadgeModule,
|
||||||
MatButtonModule,
|
MatButtonModule,
|
||||||
MatTooltipModule
|
MatTooltipModule,
|
||||||
|
MatIconModule
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class ChatModule { }
|
export class ChatModule { }
|
||||||
|
@ -4,4 +4,6 @@
|
|||||||
border-radius: 10px 0;
|
border-radius: 10px 0;
|
||||||
|
|
||||||
background-color: var(--mat-sys-background);
|
background-color: var(--mat-sys-background);
|
||||||
|
|
||||||
|
overflow-y: scroll;
|
||||||
}
|
}
|
@ -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 { Message } from '../../../models/message';
|
||||||
import { ChatService } from '../../../services/chat.service';
|
import { ChatService } from '../../../services/chat.service';
|
||||||
import { UserService } from '../../../services/user.service';
|
|
||||||
import { Channel } from '../../../models/channel';
|
import { Channel } from '../../../models/channel';
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
|
|
||||||
@ -21,7 +20,7 @@ export class FeedComponent implements OnChanges, OnDestroy {
|
|||||||
|
|
||||||
constructor(private chatService: ChatService) { }
|
constructor(private chatService: ChatService) { }
|
||||||
|
|
||||||
ngOnChanges() {
|
ngOnChanges(): void {
|
||||||
if (this.subscription) {
|
if (this.subscription) {
|
||||||
this.subscription.unsubscribe();
|
this.subscription.unsubscribe();
|
||||||
this.messages = [];
|
this.messages = [];
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<div class="message-container">
|
<div class="message-container">
|
||||||
<app-profile-picture [username]="this.message.sender" />
|
<app-profile-picture [username]="this.message.sender_name" />
|
||||||
|
|
||||||
<div class="message-inner-container">
|
<div class="message-inner-container">
|
||||||
<div class="message-sender">{{this.message.sender}}</div>
|
<div class="message-sender">{{this.message.sender_name}}</div>
|
||||||
<div class="message-content">{{this.message.content}}</div>
|
<div class="message-content">{{this.message.content}}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -1 +1,2 @@
|
|||||||
<img [src]="this.url" [alt]="this.username">
|
<img *ngIf="this.url != ''" [src]="this.url" [alt]="this.username">
|
||||||
|
<mat-icon *ngIf="this.url == ''">face</mat-icon>
|
@ -1,7 +1,23 @@
|
|||||||
img {
|
:host {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
aspect-ratio: 1;
|
||||||
|
|
||||||
border-radius: 50%;
|
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;
|
||||||
}
|
}
|
@ -1,9 +1,7 @@
|
|||||||
import { Timestamp } from "rxjs"
|
|
||||||
|
|
||||||
export class Message {
|
export class Message {
|
||||||
public id!: number
|
public id!: number
|
||||||
public sender!: string
|
public sender_name!: string
|
||||||
public channel!: number
|
public channel_id!: number
|
||||||
public time!: Date
|
public time!: Date
|
||||||
public content!: string
|
public content!: string
|
||||||
}
|
}
|
@ -1,6 +1,6 @@
|
|||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { inject, Injectable } from '@angular/core';
|
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 { LoginResponse, RegisterResponse } from './responses/auth';
|
||||||
import { ActivatedRouteSnapshot, CanActivateFn, Router, RouterStateSnapshot } from '@angular/router';
|
import { ActivatedRouteSnapshot, CanActivateFn, Router, RouterStateSnapshot } from '@angular/router';
|
||||||
import { environment } from '../../environment/environment';
|
import { environment } from '../../environment/environment';
|
||||||
@ -14,6 +14,8 @@ export class AuthService {
|
|||||||
private readonly PASSWORD_FIELD: string = "password";
|
private readonly PASSWORD_FIELD: string = "password";
|
||||||
private readonly REPEAT_PASSWORD_FIELD: string = "repeatPassword";
|
private readonly REPEAT_PASSWORD_FIELD: string = "repeatPassword";
|
||||||
|
|
||||||
|
private loggedIn: boolean = false;
|
||||||
|
|
||||||
constructor(private http: HttpClient) { }
|
constructor(private http: HttpClient) { }
|
||||||
|
|
||||||
public Login(username: string, password: string): Observable<LoginResponse> {
|
public Login(username: string, password: string): Observable<LoginResponse> {
|
||||||
@ -51,29 +53,40 @@ export class AuthService {
|
|||||||
|
|
||||||
return this.http.get(url, { withCredentials: true }).pipe(
|
return this.http.get(url, { withCredentials: true }).pipe(
|
||||||
map(() => true),
|
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 cookies = document.cookie.split(';');
|
||||||
let found = false;
|
let found = false;
|
||||||
cookies.forEach(cookie => {
|
cookies.forEach(cookie => {
|
||||||
cookie = cookie.trim();
|
cookie = cookie.trim();
|
||||||
found = found || cookie.startsWith(this.SESSION_COOKIE + "=") && cookie.split('=', 2)[1] != '';
|
found = found || cookie.startsWith(this.SESSION_COOKIE + "=") && cookie.split('=', 2)[1] != '';
|
||||||
});
|
});
|
||||||
|
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IsLoggedIn(): boolean { return this.loggedIn; }
|
||||||
}
|
}
|
||||||
|
|
||||||
export const IsLoggedInCanActivate: CanActivateFn = (
|
export const IsLoggedInCanActivate: CanActivateFn = (
|
||||||
_: ActivatedRouteSnapshot,
|
_: ActivatedRouteSnapshot,
|
||||||
__: RouterStateSnapshot
|
__: RouterStateSnapshot
|
||||||
) => {
|
): Observable<boolean> => {
|
||||||
if (inject(AuthService).IsLoggedIn()) {
|
const authService = inject(AuthService);
|
||||||
return true;
|
const router = inject(Router);
|
||||||
} else {
|
|
||||||
inject(Router).navigateByUrl("auth/login");
|
return authService.Bump().pipe(
|
||||||
return false;
|
map(isValid => {
|
||||||
}
|
if (isValid) {
|
||||||
}
|
return true;
|
||||||
|
} else {
|
||||||
|
router.navigate(['auth/login']);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
@ -1,10 +1,11 @@
|
|||||||
import { Injectable } from '@angular/core';
|
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 { Channel } from '../models/channel';
|
||||||
import { Message } from '../models/message';
|
import { Message } from '../models/message';
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { environment } from '../../environment/environment';
|
import { environment } from '../../environment/environment';
|
||||||
import { ListAvailableChannelsResponse } from './responses/chat';
|
import { GetMessagesResponse, ListAvailableChannelsResponse } from './responses/chat';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@ -32,143 +33,30 @@ export class ChatService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: implement
|
|
||||||
public GetMessages(channelID: number): Observable<Message> {
|
public GetMessages(channelID: number): Observable<Message> {
|
||||||
|
let url = `${environment.apiBase}/chat/messages/${channelID}`;
|
||||||
|
let messages: Observable<Message[]> =
|
||||||
|
this.http.get<GetMessagesResponse>(url, { withCredentials: true })
|
||||||
|
.pipe(
|
||||||
|
map(response => {
|
||||||
|
if (response.error) {
|
||||||
|
throw new Error(response.error);
|
||||||
|
}
|
||||||
|
|
||||||
return from([
|
if (response.messages) {
|
||||||
{
|
return response.messages;
|
||||||
id: 1,
|
}
|
||||||
sender: 'admin',
|
|
||||||
channel: 1,
|
throw new Error("bad API response, missing messages with no error");
|
||||||
time: new Date(),
|
}),
|
||||||
content: 'this is my first message'
|
catchError(error => throwError(() => new Error(error.error.message)))
|
||||||
},
|
);
|
||||||
{
|
|
||||||
id: 1,
|
url = `${environment.apiBase}/chat/subscribe/${channelID}`;
|
||||||
sender: 'alice',
|
let socket = webSocket<Message>(url);
|
||||||
channel: 1,
|
|
||||||
time: new Date(),
|
return merge(
|
||||||
content: 'this is my first message'
|
messages.pipe(mergeMap(msgArray => from(msgArray))),
|
||||||
},
|
socket.asObservable());
|
||||||
{
|
|
||||||
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'
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
import { Channel } from "../../models/channel";
|
import { Channel } from "../../models/channel";
|
||||||
|
import { Message } from "../../models/message";
|
||||||
import { APIResponse } from "./basic";
|
import { APIResponse } from "./basic";
|
||||||
|
|
||||||
export class ListAvailableChannelsResponse extends APIResponse {
|
export class ListAvailableChannelsResponse extends APIResponse {
|
||||||
public channels?: Channel[];
|
public channels?: Channel[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class GetMessagesResponse extends APIResponse {
|
||||||
|
public messages?: Message[];
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
export const environment = {
|
export const environment = {
|
||||||
production: false,
|
production: false,
|
||||||
apiBase: "http://localhost:5000"
|
apiBase: "http://localhost:5000/api"
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user