Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 673269e2b8 | |||
| 6ffcece3fa |
@@ -28,4 +28,8 @@ export class discussionsService {
|
||||
createPrivateDiscussion(username: string): Observable<Discussion> {
|
||||
return this.http.post<Discussion>(`${this.apiUrl}/discussions/private`, { username });
|
||||
}
|
||||
|
||||
createGroupDiscussion(groupName: string, usernames: string[]): Observable<Discussion> {
|
||||
return this.http.post<Discussion>(`${this.apiUrl}/discussions/group`, { groupName, usernames });
|
||||
}
|
||||
}
|
||||
@@ -107,4 +107,159 @@
|
||||
transform: scale(0.97);
|
||||
box-shadow: 0 1px 5px rgba(180, 80, 80, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
.mode-switch {
|
||||
margin-top: auto;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
padding: 14px 16px;
|
||||
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 16px;
|
||||
|
||||
backdrop-filter: blur(8px);
|
||||
box-shadow: 0 2px 12px rgba(122, 46, 46, 0.08);
|
||||
}
|
||||
|
||||
.switch-label {
|
||||
color: #7a2e2e;
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
/* SWITCH */
|
||||
|
||||
.switch {
|
||||
position: relative;
|
||||
width: 58px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.switch input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.slider {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
background: rgba(255, 255, 255, 0.6);
|
||||
border-radius: 999px;
|
||||
|
||||
transition: all 0.25s ease;
|
||||
|
||||
box-shadow:
|
||||
inset 0 1px 3px rgba(0,0,0,0.08),
|
||||
0 2px 8px rgba(0,0,0,0.05);
|
||||
}
|
||||
|
||||
.slider::before {
|
||||
content: "";
|
||||
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
left: 4px;
|
||||
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
|
||||
border-radius: 50%;
|
||||
background: white;
|
||||
|
||||
transition: all 0.25s ease;
|
||||
|
||||
box-shadow:
|
||||
0 2px 8px rgba(0,0,0,0.15);
|
||||
}
|
||||
|
||||
.switch input:checked + .slider {
|
||||
background: #bd5a5a;
|
||||
}
|
||||
|
||||
.switch input:checked + .slider::before {
|
||||
transform: translateX(26px);
|
||||
}
|
||||
|
||||
.mode-switch:hover .slider {
|
||||
transform: scale(1.03);
|
||||
}
|
||||
|
||||
.add-member-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
|
||||
width: 100%;
|
||||
|
||||
padding: 12px 16px;
|
||||
|
||||
border: 2px dashed rgba(189, 90, 90, 0.35);
|
||||
border-radius: 14px;
|
||||
|
||||
background: rgba(255, 255, 255, 0.55);
|
||||
|
||||
color: #bd5a5a;
|
||||
font-size: 0.95rem;
|
||||
font-weight: 600;
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
transition: all 0.2s ease;
|
||||
|
||||
ion-icon {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
border-color: #bd5a5a;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
}
|
||||
|
||||
.remove-btn {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
|
||||
flex-shrink: 0;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
|
||||
background: white;
|
||||
color: #bd5a5a;
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
box-shadow: 0 2px 8px rgba(180, 80, 80, 0.12);
|
||||
|
||||
transition: all 0.2s ease;
|
||||
|
||||
ion-icon {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 12px rgba(180, 80, 80, 0.18);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: scale(0.92);
|
||||
}
|
||||
}
|
||||
@@ -11,30 +11,91 @@
|
||||
<div class="modal-layout">
|
||||
|
||||
<div class="modal-header">
|
||||
<h2 class="modal-title">Nouvelle conversation</h2>
|
||||
<button class="modal-close-btn" (click)="closeNav()">
|
||||
<ion-icon name="close-outline" />
|
||||
</button>
|
||||
<h2 class="modal-title">{{ isGroup() ? 'Nouveau groupe' : 'Nouvelle conversation' }}</h2>
|
||||
<div class="header-actions">
|
||||
<button class="modal-close-btn" (click)="closeNav()">
|
||||
<ion-icon name="close-outline" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<div class="input-wrapper">
|
||||
<ion-item class="custom-item">
|
||||
<ion-input
|
||||
[formControl]="username"
|
||||
label="Nom d'utilisateur"
|
||||
labelPlacement="floating"
|
||||
placeholder="ex: jean_dupont"
|
||||
/>
|
||||
</ion-item>
|
||||
|
||||
<!-- Mode privé -->
|
||||
<ng-container *ngIf="!isGroup()">
|
||||
<div class="input-wrapper">
|
||||
<ion-item class="custom-item">
|
||||
<ion-input
|
||||
[formControl]="username"
|
||||
label="Nom d'utilisateur"
|
||||
labelPlacement="floating"
|
||||
placeholder="ex: jean_dupont"
|
||||
/>
|
||||
</ion-item>
|
||||
<p class="error-msg" *ngIf="errorMsg()">{{ errorMsg() }}</p>
|
||||
</div>
|
||||
<button class="submit-btn" (click)="startConversation()" [disabled]="isLoading()">
|
||||
{{ isLoading() ? 'Création...' : 'Démarrer la conversation' }}
|
||||
</button>
|
||||
</ng-container>
|
||||
|
||||
<!-- Mode groupe -->
|
||||
<ng-container *ngIf="isGroup()">
|
||||
<div class="input-wrapper">
|
||||
<ion-item class="custom-item">
|
||||
<ion-input
|
||||
[formControl]="groupName"
|
||||
label="Nom du groupe"
|
||||
labelPlacement="floating"
|
||||
placeholder="ex: Les amis"
|
||||
/>
|
||||
</ion-item>
|
||||
</div>
|
||||
|
||||
<div class="members-list">
|
||||
<div class="member-row" *ngFor="let ctrl of groupMembers; let i = index">
|
||||
<ion-item class="custom-item member-item">
|
||||
<ion-input
|
||||
[formControl]="ctrl"
|
||||
label="Membre {{ i + 1 }}"
|
||||
labelPlacement="floating"
|
||||
placeholder="ex: jean_dupont"
|
||||
/>
|
||||
</ion-item>
|
||||
<button class="remove-btn" (click)="removeMember(i)" *ngIf="groupMembers.length > 1">
|
||||
<ion-icon name="close-outline" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="add-member-btn" (click)="addMember()" *ngIf="groupMembers.length < 10">
|
||||
<ion-icon name="add-outline" />
|
||||
Ajouter un membre
|
||||
</button>
|
||||
|
||||
<p class="error-msg" *ngIf="errorMsg()">{{ errorMsg() }}</p>
|
||||
|
||||
<button class="submit-btn" (click)="startGroupConversation()" [disabled]="isLoading()">
|
||||
{{ isLoading() ? 'Création...' : 'Créer le groupe' }}
|
||||
</button>
|
||||
</ng-container>
|
||||
|
||||
<div class="mode-switch">
|
||||
<span class="switch-label">
|
||||
{{ isGroup() ? 'Mode groupe' : 'Conversation privée' }}
|
||||
</span>
|
||||
|
||||
<label class="switch">
|
||||
<input
|
||||
type="checkbox"
|
||||
[checked]="isGroup()"
|
||||
(change)="toggleMode()"
|
||||
>
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<button class="submit-btn" (click)="startConversation()" [disabled]="isLoading()">
|
||||
{{ isLoading() ? 'Création...' : 'Démarrer la conversation' }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</ng-template>
|
||||
</ion-modal>
|
||||
@@ -2,14 +2,14 @@ import { Component, inject, signal } from '@angular/core';
|
||||
import { Router } from "@angular/router";
|
||||
import { ReactiveFormsModule, FormControl } from "@angular/forms";
|
||||
import { addIcons } from "ionicons";
|
||||
import { closeOutline } from "ionicons/icons";
|
||||
import { IonIcon, IonInput, IonItem, IonModal } from "@ionic/angular/standalone";
|
||||
import { closeOutline, addOutline } from "ionicons/icons";
|
||||
import { discussionsService } from "../../../core/chat/discussion.service";
|
||||
import { CommonModule } from "@angular/common";
|
||||
import {CommonModule} from "@angular/common";
|
||||
|
||||
@Component({
|
||||
selector: 'app-menu-nav',
|
||||
imports: [CommonModule, IonModal, IonItem, IonInput, IonIcon, ReactiveFormsModule],
|
||||
imports: [IonModal, IonItem, IonInput, IonIcon, ReactiveFormsModule, CommonModule],
|
||||
templateUrl: './menu-nav.component.html',
|
||||
styleUrl: './menu-nav.component.css'
|
||||
})
|
||||
@@ -21,11 +21,17 @@ export class MenuNav {
|
||||
isModalOpen = signal(false);
|
||||
isLoading = signal(false);
|
||||
errorMsg = signal<string | null>(null);
|
||||
isGroup = signal(false);
|
||||
|
||||
// Mode privé
|
||||
username = new FormControl('');
|
||||
|
||||
// Mode groupe
|
||||
groupName = new FormControl('');
|
||||
groupMembers: FormControl[] = [new FormControl('')];
|
||||
|
||||
constructor() {
|
||||
addIcons({ closeOutline });
|
||||
addIcons({ closeOutline, addOutline });
|
||||
}
|
||||
|
||||
openNav() { this.isModalOpen.set(true); }
|
||||
@@ -33,7 +39,25 @@ export class MenuNav {
|
||||
closeNav() {
|
||||
this.isModalOpen.set(false);
|
||||
this.username.reset();
|
||||
this.groupName.reset();
|
||||
this.groupMembers = [new FormControl('')];
|
||||
this.errorMsg.set(null);
|
||||
this.isGroup.set(false);
|
||||
}
|
||||
|
||||
toggleMode() {
|
||||
this.isGroup.update(v => !v);
|
||||
this.errorMsg.set(null);
|
||||
}
|
||||
|
||||
addMember() {
|
||||
if (this.groupMembers.length < 10) {
|
||||
this.groupMembers.push(new FormControl(''));
|
||||
}
|
||||
}
|
||||
|
||||
removeMember(index: number) {
|
||||
this.groupMembers.splice(index, 1);
|
||||
}
|
||||
|
||||
startConversation() {
|
||||
@@ -57,4 +81,30 @@ export class MenuNav {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
startGroupConversation() {
|
||||
const name = this.groupName.value?.trim();
|
||||
const usernames = this.groupMembers
|
||||
.map(c => c.value?.trim())
|
||||
.filter(v => !!v);
|
||||
|
||||
if (!name || usernames.length === 0 || this.isLoading()) return;
|
||||
|
||||
this.isLoading.set(true);
|
||||
this.errorMsg.set(null);
|
||||
|
||||
this.discussionService.createGroupDiscussion(name, usernames).subscribe({
|
||||
next: (discussion) => {
|
||||
this.isLoading.set(false);
|
||||
this.closeNav();
|
||||
this.router.navigate(['/main/messages', discussion.id]);
|
||||
},
|
||||
error: (err) => {
|
||||
this.isLoading.set(false);
|
||||
this.errorMsg.set(
|
||||
err.status === 404 ? 'Un ou plusieurs utilisateurs introuvables.' : 'Une erreur est survenue.'
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -2,5 +2,5 @@
|
||||
<div class="icon-wrapper">
|
||||
<img width="50" height="50" src="https://img.icons8.com/ios/50/user-male-circle--v1.png" alt="user"/>
|
||||
</div>
|
||||
<span class="username">Nom User</span>
|
||||
<span class="username">{{ name || 'Utilisateur' }}</span>
|
||||
</button>
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Component } from '@angular/core';
|
||||
import {Component, inject, Input} from '@angular/core';
|
||||
import {AuthService} from "../../../core/auth/auth.service";
|
||||
|
||||
@Component({
|
||||
selector: 'app-messages-infouser',
|
||||
@@ -7,5 +8,8 @@ import { Component } from '@angular/core';
|
||||
styleUrl: './messages-infouser.component.css'
|
||||
})
|
||||
export class MessagesInfoUser {
|
||||
@Input() name: string = '';
|
||||
private authService = inject(AuthService);
|
||||
|
||||
user = this.authService.currentUser;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<div class="header">
|
||||
<app-messages-menu/>
|
||||
<app-messages-infouser/>
|
||||
<app-messages-infouser [name]="discussionName"/>
|
||||
</div>
|
||||
|
||||
<div class="messages">
|
||||
|
||||
@@ -22,11 +22,19 @@ export class MessagesMain implements OnInit {
|
||||
currentDiscussionId!: string;
|
||||
currentUserId!: number;
|
||||
messages: Message[] = [];
|
||||
discussionName: string = '';
|
||||
|
||||
ngOnInit() {
|
||||
this.currentDiscussionId = this.route.snapshot.paramMap.get('discussionId')!;
|
||||
this.currentUserId = this.authService.getCurrentUserId();
|
||||
|
||||
this.discussionService.getDiscussions().subscribe({
|
||||
next: (discussions) => {
|
||||
const discussion = discussions.find(d => d.id === +this.currentDiscussionId);
|
||||
this.discussionName = discussion?.name ?? '';
|
||||
}
|
||||
});
|
||||
|
||||
this.discussionService.getMessages(this.currentDiscussionId).subscribe({
|
||||
next: (messages) => this.messages = messages,
|
||||
error: (err) => console.error('Impossible de charger les messages', err)
|
||||
|
||||
@@ -39,6 +39,7 @@ model/knots-dto-user-update-user-description-dto.ts
|
||||
model/knots-dto-user-update-user-password-dto.ts
|
||||
model/knots-dto-user-update-user-profile-picture-dto.ts
|
||||
model/knots-dto-user-update-username-dto.ts
|
||||
model/knots-endpoints-discussion-create-group-discussion-request.ts
|
||||
model/knots-endpoints-discussion-create-private-discussion-request.ts
|
||||
model/models.ts
|
||||
param.ts
|
||||
|
||||
@@ -25,6 +25,8 @@ import { KnotsDTODiscussionDeleteDiscussionDto } from '../model/knots-dto-discus
|
||||
// @ts-ignore
|
||||
import { KnotsDTOMessageGetMessageDetailsDto } from '../model/knots-dto-message-get-message-details-dto';
|
||||
// @ts-ignore
|
||||
import { KnotsEndpointsDiscussionCreateGroupDiscussionRequest } from '../model/knots-endpoints-discussion-create-group-discussion-request';
|
||||
// @ts-ignore
|
||||
import { KnotsEndpointsDiscussionCreatePrivateDiscussionRequest } from '../model/knots-endpoints-discussion-create-private-discussion-request';
|
||||
|
||||
// @ts-ignore
|
||||
@@ -108,6 +110,74 @@ export class DiscussionsService extends BaseService {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @endpoint post /API/discussions/group
|
||||
* @param knotsEndpointsDiscussionCreateGroupDiscussionRequest
|
||||
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
|
||||
* @param reportProgress flag to report request and response progress.
|
||||
* @param options additional options
|
||||
*/
|
||||
public createGroupDiscussionEndpoint(knotsEndpointsDiscussionCreateGroupDiscussionRequest: KnotsEndpointsDiscussionCreateGroupDiscussionRequest, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable<object>;
|
||||
public createGroupDiscussionEndpoint(knotsEndpointsDiscussionCreateGroupDiscussionRequest: KnotsEndpointsDiscussionCreateGroupDiscussionRequest, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable<HttpResponse<object>>;
|
||||
public createGroupDiscussionEndpoint(knotsEndpointsDiscussionCreateGroupDiscussionRequest: KnotsEndpointsDiscussionCreateGroupDiscussionRequest, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable<HttpEvent<object>>;
|
||||
public createGroupDiscussionEndpoint(knotsEndpointsDiscussionCreateGroupDiscussionRequest: KnotsEndpointsDiscussionCreateGroupDiscussionRequest, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable<any> {
|
||||
if (knotsEndpointsDiscussionCreateGroupDiscussionRequest === null || knotsEndpointsDiscussionCreateGroupDiscussionRequest === undefined) {
|
||||
throw new Error('Required parameter knotsEndpointsDiscussionCreateGroupDiscussionRequest was null or undefined when calling createGroupDiscussionEndpoint.');
|
||||
}
|
||||
|
||||
let localVarHeaders = this.defaultHeaders;
|
||||
|
||||
// authentication (JWTBearerAuth) required
|
||||
localVarHeaders = this.configuration.addCredentialToHeaders('JWTBearerAuth', 'Authorization', localVarHeaders, 'Bearer ');
|
||||
|
||||
const localVarHttpHeaderAcceptSelected: string | undefined = options?.httpHeaderAccept ?? this.configuration.selectHeaderAccept([
|
||||
'application/json'
|
||||
]);
|
||||
if (localVarHttpHeaderAcceptSelected !== undefined) {
|
||||
localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
|
||||
}
|
||||
|
||||
const localVarHttpContext: HttpContext = options?.context ?? new HttpContext();
|
||||
|
||||
const localVarTransferCache: boolean = options?.transferCache ?? true;
|
||||
|
||||
|
||||
// to determine the Content-Type header
|
||||
const consumes: string[] = [
|
||||
'application/json'
|
||||
];
|
||||
const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes);
|
||||
if (httpContentTypeSelected !== undefined) {
|
||||
localVarHeaders = localVarHeaders.set('Content-Type', httpContentTypeSelected);
|
||||
}
|
||||
|
||||
let responseType_: 'text' | 'json' | 'blob' = 'json';
|
||||
if (localVarHttpHeaderAcceptSelected) {
|
||||
if (localVarHttpHeaderAcceptSelected.startsWith('text')) {
|
||||
responseType_ = 'text';
|
||||
} else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) {
|
||||
responseType_ = 'json';
|
||||
} else {
|
||||
responseType_ = 'blob';
|
||||
}
|
||||
}
|
||||
|
||||
let localVarPath = `/API/discussions/group`;
|
||||
const { basePath, withCredentials } = this.configuration;
|
||||
return this.httpClient.request<object>('post', `${basePath}${localVarPath}`,
|
||||
{
|
||||
context: localVarHttpContext,
|
||||
body: knotsEndpointsDiscussionCreateGroupDiscussionRequest,
|
||||
responseType: <any>responseType_,
|
||||
...(withCredentials ? { withCredentials } : {}),
|
||||
headers: localVarHeaders,
|
||||
observe: observe,
|
||||
...(localVarTransferCache !== undefined ? { transferCache: localVarTransferCache } : {}),
|
||||
reportProgress: reportProgress
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @endpoint post /API/discussions/private
|
||||
* @param knotsEndpointsDiscussionCreatePrivateDiscussionRequest
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Knots
|
||||
*
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
export interface KnotsEndpointsDiscussionCreateGroupDiscussionRequest {
|
||||
groupName?: string;
|
||||
usernames?: Array<string>;
|
||||
}
|
||||
|
||||
@@ -23,4 +23,5 @@ export * from './knots-dto-user-update-user-description-dto';
|
||||
export * from './knots-dto-user-update-user-password-dto';
|
||||
export * from './knots-dto-user-update-user-profile-picture-dto';
|
||||
export * from './knots-dto-user-update-username-dto';
|
||||
export * from './knots-endpoints-discussion-create-group-discussion-request';
|
||||
export * from './knots-endpoints-discussion-create-private-discussion-request';
|
||||
|
||||
Reference in New Issue
Block a user