login et register + chat et discussion services

This commit is contained in:
gokhoal
2026-06-10 13:21:54 +02:00
parent 60d8e5d588
commit eb87d15f94
7 changed files with 136 additions and 55 deletions
+15 -2
View File
@@ -2,7 +2,7 @@
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { firstValueFrom } from 'rxjs'; import { firstValueFrom } from 'rxjs';
import {LoggedUser} from "../../models/user.model"; import { LoggedUser } from "../../models/user.model";
interface LoginResponse { interface LoginResponse {
token: string; token: string;
@@ -14,6 +14,11 @@ interface LoginResponse {
description: string | null; description: string | null;
} }
interface RegisterRequest {
username: string;
password: string;
}
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class AuthService { export class AuthService {
private http = inject(HttpClient); private http = inject(HttpClient);
@@ -56,7 +61,15 @@ export class AuthService {
localStorage.setItem(this.USER_KEY, JSON.stringify(user)); localStorage.setItem(this.USER_KEY, JSON.stringify(user));
this.currentUser.set(user); this.currentUser.set(user);
await this.router.navigate(['/']); // adapte la route await this.router.navigate(['/main/menu']);
}
async register(username: string, password: string): Promise<void> {
await firstValueFrom(
this.http.post<void>('/API/users/register', { username, password } as RegisterRequest)
);
await this.login(username, password);
} }
logout(): void { logout(): void {
@@ -5,7 +5,7 @@ import {
import { CommonModule } from "@angular/common"; import { CommonModule } from "@angular/common";
import { FormBuilder, ReactiveFormsModule, Validators } from "@angular/forms"; import { FormBuilder, ReactiveFormsModule, Validators } from "@angular/forms";
import { Router } from "@angular/router"; import { Router } from "@angular/router";
import { LoginService } from "../../services/api/login.service"; import { AuthService } from "../../core/auth/auth.service";
@Component({ @Component({
selector: 'login-form', selector: 'login-form',
@@ -16,9 +16,8 @@ import { LoginService } from "../../services/api/login.service";
export class LoginFormComponent { export class LoginFormComponent {
private fb = inject(FormBuilder); private fb = inject(FormBuilder);
private loginService = inject(LoginService); private authService = inject(AuthService);
private router = inject(Router); private router = inject(Router);
private navCtrl = inject(NavController);
isLoading = false; isLoading = false;
@@ -35,26 +34,20 @@ export class LoginFormComponent {
if (this.loginForm.valid) { if (this.loginForm.valid) {
this.isLoading = true; this.isLoading = true;
const request = { try {
name: this.loginForm.value.name!, await this.authService.login(
password: this.loginForm.value.password! this.loginForm.value.name!,
}; this.loginForm.value.password!
);
this.loginService.login(request).subscribe({ } catch (err: any) {
next: (response) => { if (err.status === 401) {
this.isLoading = false; console.error('Identifiant ou mot de passe incorrect.');
// Le discussionId vient de la réponse de l'API } else {
this.navCtrl.navigateRoot(['main/messages', response.discussionId]); console.error('Impossible de contacter le serveur.');
},
error: (err) => {
this.isLoading = false;
if (err.status === 401) {
console.error('Identifiant ou mot de passe incorrect.');
} else {
console.error('Impossible de contacter le serveur.');
}
} }
}); } finally {
this.isLoading = false;
}
} else { } else {
Object.values(this.loginForm.controls).forEach(control => { Object.values(this.loginForm.controls).forEach(control => {
@@ -12,7 +12,7 @@ export class MessagesSend {
private chatService = inject(ChatService); private chatService = inject(ChatService);
@Input() discussionId!: string; // passé par le composant parent @Input() discussionId!: string;
sendMessage = new FormControl(); sendMessage = new FormControl();
isSending = false; isSending = false;
@@ -21,7 +21,7 @@ export class MessagesSend {
const message = this.sendMessage.value; const message = this.sendMessage.value;
if (!message || message.trim() === '') return; if (!message || message.trim() === '') return;
if (this.isSending) return; // évite le double envoi if (this.isSending) return;
this.isSending = true; this.isSending = true;
@@ -30,7 +30,6 @@ export class MessagesSend {
this.sendMessage.reset(); this.sendMessage.reset();
} catch (error) { } catch (error) {
console.error('Erreur lors de l\'envoi :', error); console.error('Erreur lors de l\'envoi :', error);
// Afficher une notification d'erreur ici
} finally { } finally {
this.isSending = false; this.isSending = false;
} }
@@ -1,5 +1,4 @@
<ion-content [fullscreen]="true" class="bg"> <ion-content [fullscreen]="true" class="bg">
<div class="container"> <div class="container">
<div class="logo-box"> <div class="logo-box">
@@ -8,34 +7,37 @@
<h1>Knots</h1> <h1>Knots</h1>
<div class="card"> <div class="card" [formGroup]="registerForm">
<h3>Inscrivez-vous à Knots !</h3> <h3>Inscrivez-vous à Knots !</h3>
<p class="subtitle">Commencez à nouer des liens !</p> <p class="subtitle">Commencez à nouer des liens !</p>
<ion-item lines="none" class="input"> <ion-item lines="none" class="input">
<ion-input placeholder="Nom d'utilisateur..."></ion-input> <ion-input placeholder="Nom d'utilisateur..." formControlName="username"></ion-input>
</ion-item> </ion-item>
<ion-item lines="none" class="input"> <ion-item lines="none" class="input">
<ion-input type="password" placeholder="Mot de passe..."></ion-input> <ion-input type="password" placeholder="Mot de passe..." formControlName="password"></ion-input>
</ion-item> </ion-item>
<ion-item lines="none" class="input"> <ion-item lines="none" class="input">
<ion-input type="password" placeholder="Confirmez votre mot de passe..."></ion-input> <ion-input type="password" placeholder="Confirmez votre mot de passe..." formControlName="confirmPassword"></ion-input>
</ion-item> </ion-item>
<ion-button expand="block" class="btn"> <p *ngIf="registerForm.hasError('passwordMismatch')" style="color:#d97070; font-size:13px; text-align:center; margin:0;">
Créer son compte Les mots de passe ne correspondent pas.
</p>
<ion-button expand="block" class="btn" (click)="submitForm()" [disabled]="isLoading">
{{ isLoading ? 'Création...' : 'Créer son compte' }}
</ion-button> </ion-button>
</div> </div>
<div class="signup"> <div class="signup">
Déjà un compte ?<br> Déjà un compte ?<br>
<a (click)="goToLogin()" >Connectez-vous ici</a> <a (click)="goToLogin()">Connectez-vous ici</a>
</div> </div>
</div> </div>
</ion-content> </ion-content>
@@ -1,30 +1,59 @@
import {Component, inject} from '@angular/core'; import { Component, inject } from '@angular/core';
import { import {
IonButton, IonButton, IonContent, IonInput, IonItem
IonCard,
IonCardContent,
IonCardHeader,
IonCardSubtitle,
IonCardTitle, IonContent, IonInput, IonItem
} from "@ionic/angular/standalone"; } from "@ionic/angular/standalone";
import {Router} from "@angular/router"; import { Router } from "@angular/router";
import { FormBuilder, ReactiveFormsModule, Validators } from "@angular/forms";
import { CommonModule } from "@angular/common";
import {AuthService} from "../../core/auth/auth.service";
@Component({ @Component({
selector: 'app-register-form', selector: 'app-register-form',
imports: [ imports: [IonButton, IonInput, IonContent, IonItem, ReactiveFormsModule, CommonModule],
IonButton, templateUrl: './register-form.component.html',
IonInput, styleUrl: './register-form.component.css'
IonContent,
IonItem
],
templateUrl: './register-form.component.html',
styleUrl: './register-form.component.css'
}) })
export class RegisterFormComponent { export class RegisterFormComponent {
private router = inject(Router) private router = inject(Router);
private fb = inject(FormBuilder);
private authService = inject(AuthService);
isLoading = false;
registerForm = this.fb.group({
username: ['', [Validators.required, Validators.maxLength(50)]],
password: ['', [Validators.required, Validators.minLength(12), Validators.maxLength(50)]],
confirmPassword: ['', [Validators.required]]
}, { validators: this.passwordMatchValidator });
passwordMatchValidator(form: any) {
const password = form.get('password')?.value;
const confirm = form.get('confirmPassword')?.value;
return password === confirm ? null : { passwordMismatch: true };
}
goToLogin() { goToLogin() {
this.router.navigate(['/login']); this.router.navigate(['/login']);
} }
}
async submitForm() {
if (this.registerForm.valid) {
this.isLoading = true;
try {
await this.authService.register(
this.registerForm.value.username!,
this.registerForm.value.password!
);
} catch (err: any) {
if (err.status === 409) {
console.error('Ce nom d\'utilisateur est déjà pris.');
} else {
console.error('Impossible de contacter le serveur.');
}
} finally {
this.isLoading = false;
}
}
}
}
+30
View File
@@ -0,0 +1,30 @@
import { Injectable } from '@angular/core';
import { HubConnection, HubConnectionBuilder, HubConnectionState } from '@microsoft/signalr';
@Injectable({ providedIn: 'root' })
export class ChatService {
private hub: HubConnection;
constructor() {
this.hub = new HubConnectionBuilder()
.withUrl('https://localhost:5001/hubs/chat')
.withAutomaticReconnect()
.build();
}
async connect() {
if (this.hub.state === HubConnectionState.Disconnected) {
await this.hub.start();
}
}
async sendMessage(discussionId: string, content: string) {
await this.connect(); // s'assure que la connexion est active
await this.hub.invoke('SendMessage', discussionId, content);
}
onMessage(callback: (message: any) => void) {
this.hub.on('ReceiveMessage', callback);
}
}
@@ -0,0 +1,15 @@
import {Observable} from "rxjs";
import {Discussion} from "../../pages/menu/menu-users/menu-users.component";
import {HttpClient} from "@angular/common/http";
import {inject, Injectable} from "@angular/core";
@Injectable({ providedIn: 'root' })
export class discussionsService {
private http = inject(HttpClient);
private apiUrl = 'https://localhost:5001/API';
getDiscussions(): Observable<Discussion[]> {
return this.http.get<Discussion[]>(`${this.apiUrl}/discussions`);
}
}