diff --git a/android/app/build.gradle b/android/app/build.gradle
index df4cf3b..bb0d83c 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -3,7 +3,6 @@ apply plugin: 'com.android.application'
android {
namespace = "com.knots.mobile"
compileSdk = rootProject.ext.compileSdkVersion
-
defaultConfig {
applicationId "com.knots.mobile"
minSdkVersion rootProject.ext.minSdkVersion
@@ -11,19 +10,12 @@ android {
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
-
aaptOptions {
- // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
+ // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
+ // Default: https://android.googlesource.com/platform/frameworks/base/+/282e181b58cf72b6ca770dc7ca5f91f135444502/tools/aapt/AaptAssets.cpp#61
ignoreAssetsPattern = '!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~'
}
}
-
- // ✅ Forcer Java 17
- compileOptions {
- sourceCompatibility JavaVersion.VERSION_17
- targetCompatibility JavaVersion.VERSION_17
- }
-
buildTypes {
release {
minifyEnabled false
@@ -33,7 +25,7 @@ android {
}
repositories {
- flatDir {
+ flatDir{
dirs '../capacitor-cordova-android-plugins/src/main/libs', 'libs'
}
}
@@ -59,4 +51,4 @@ try {
}
} catch(Exception e) {
logger.info("google-services.json not found, google-services plugin not applied. Push Notifications won't work")
-}
\ No newline at end of file
+}
diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml
index 5f41835..b296ba3 100644
--- a/android/app/src/main/res/values/strings.xml
+++ b/android/app/src/main/res/values/strings.xml
@@ -1,7 +1,7 @@
- Knots
- Knots
+ knots
+ knots
com.knots.mobile
com.knots.mobile
diff --git a/android/build.gradle b/android/build.gradle
index b1d1d0c..f8f0e43 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -1,16 +1,7 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
-
- allprojects {
- gradle.projectsEvaluated {
- tasks.withType(JavaCompile) {
- sourceCompatibility = JavaVersion.VERSION_17
- targetCompatibility = JavaVersion.VERSION_17
- }
- }
- }
-
+
repositories {
google()
mavenCentral()
diff --git a/capacitor.config.ts b/capacitor.config.ts
index 5e7c076..17717c3 100644
--- a/capacitor.config.ts
+++ b/capacitor.config.ts
@@ -2,8 +2,8 @@ import type { CapacitorConfig } from '@capacitor/cli';
const config: CapacitorConfig = {
appId: 'com.knots.mobile',
- appName: 'Knots',
- webDir: 'dist/knots-front/browser'
+ appName: 'knots',
+ webDir: 'dist'
};
export default config;
diff --git a/package-lock.json b/package-lock.json
index ce0fa02..c76d3ae 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -247,7 +247,6 @@
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.23.tgz",
"integrity": "sha512-RazHPQkUEsNU/OZ75w9UeHxGFMthRiuAW2B/uA7eXExBj/1meHrrBfoCA56ujW2GUxVjRtSrMjylKh4R4meiYA==",
"license": "MIT",
- "peer": true,
"dependencies": {
"ajv": "8.18.0",
"ajv-formats": "3.0.1",
@@ -284,7 +283,6 @@
"resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.2.23.tgz",
"integrity": "sha512-Jzs7YM4X6azmHU7Mw5tQSPMuvaqYS8SLnZOJbtiXCy1JyuW9bm/WBBecNHMiuZ8LHXKhvQ6AVX1tKrzF6uiDmw==",
"license": "MIT",
- "peer": true,
"dependencies": {
"@angular-devkit/core": "19.2.23",
"jsonc-parser": "3.3.1",
@@ -432,7 +430,6 @@
"resolved": "https://registry.npmjs.org/@angular/common/-/common-19.2.20.tgz",
"integrity": "sha512-1M3W3FjUUbVKXDMs+yQpBhnkD/pCe0Jn79rPE5W+EGWWxFoLSyGX+fhnRO5m4c9k66p3nvYrikWQ0ZzMv3M5tw==",
"license": "MIT",
- "peer": true,
"dependencies": {
"tslib": "^2.3.0"
},
@@ -449,7 +446,6 @@
"resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-19.2.20.tgz",
"integrity": "sha512-LvjE8W58EACgTFaAoqmNe7FRsbvoQ0GvCB/rmm6AEMWx/0W/JBvWkQTrOQlwpoeYOHcMZRGdmPcZoUDwU3JySQ==",
"license": "MIT",
- "peer": true,
"dependencies": {
"tslib": "^2.3.0"
},
@@ -463,7 +459,6 @@
"integrity": "sha512-tYYQk8AUz2sEkVl0a3uebduDUXPuKiGEKl2Jryrbn0xh9i1EsxoCjt1VvHnGnksGp3mz4DQihFVEnte0KeVQ5g==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@babel/core": "7.26.9",
"@jridgewell/sourcemap-codec": "^1.4.14",
@@ -540,7 +535,6 @@
"resolved": "https://registry.npmjs.org/@angular/core/-/core-19.2.20.tgz",
"integrity": "sha512-pxzQh8ouqfE57lJlXjIzXFuRETwkfMVwS+NFCfv2yh01Qtx+vymO8ZClcJMgLPfBYinhBYX+hrRYVSa1nzlkRQ==",
"license": "MIT",
- "peer": true,
"dependencies": {
"tslib": "^2.3.0"
},
@@ -557,7 +551,6 @@
"resolved": "https://registry.npmjs.org/@angular/forms/-/forms-19.2.20.tgz",
"integrity": "sha512-agi7InbMzop1jrud6L7SlNwnZk3iNolORcFIwBQMvKxLkcJ+ttbSYuM0KAw56IundWHf4dL9GP4cSygm4kUeFA==",
"license": "MIT",
- "peer": true,
"dependencies": {
"tslib": "^2.3.0"
},
@@ -576,7 +569,6 @@
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-19.2.20.tgz",
"integrity": "sha512-O9ZoQKILPC1T2c64OASS75XlOLBxY81m5AAgsBKhwiFWq+V28RsO0cnwpi1YSh/z4ryH8Fe7IUFz8jGrsJi3hQ==",
"license": "MIT",
- "peer": true,
"dependencies": {
"tslib": "^2.3.0"
},
@@ -617,7 +609,6 @@
"resolved": "https://registry.npmjs.org/@angular/router/-/router-19.2.20.tgz",
"integrity": "sha512-y0fyKycxJHr82kxXKE50Vac5hPn5Kx3gw9CfqyEuwJ9VQzEixDljU+chrQK4Wods14jJn9Tt2ncNPGH1rLya3Q==",
"license": "MIT",
- "peer": true,
"dependencies": {
"tslib": "^2.3.0"
},
@@ -662,7 +653,6 @@
"integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@ampproject/remapping": "^2.2.0",
"@babel/code-frame": "^7.26.2",
@@ -2519,7 +2509,6 @@
"resolved": "https://registry.npmjs.org/@capacitor/core/-/core-8.3.1.tgz",
"integrity": "sha512-UF8ItlHguU1Z6GXfPTeT2gakf+ctNI8pAS1kwSBQlsJMlfD4OPoto/SmKnOxKCQvnF4WRcdWeg6C0zREUNaAQg==",
"license": "MIT",
- "peer": true,
"dependencies": {
"tslib": "^2.1.0"
}
@@ -3213,7 +3202,6 @@
"integrity": "sha512-G1ytyOoHh5BphmEBxSwALin3n1KGNYB6yImbICcRQdzXfOGbuJ9Jske/Of5Sebk339NSGGNfUshnzK8YWkTPsQ==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@inquirer/checkbox": "^4.1.2",
"@inquirer/confirm": "^5.1.6",
@@ -4965,7 +4953,6 @@
"resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.17.tgz",
"integrity": "sha512-hLODw5Abp8OQgA+mUO4tHou4krKgDtUcM9j5Ihxncst9XeyxYBTt2bwZm4e4EQr5E352S4Fyy6V3iFx9ggxKAg==",
"license": "MIT",
- "peer": true,
"dependencies": {
"file-type": "21.3.2",
"iterare": "1.2.1",
@@ -7014,7 +7001,6 @@
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
"dev": true,
"license": "MIT",
- "peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -7078,7 +7064,6 @@
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"license": "MIT",
- "peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
@@ -7309,7 +7294,6 @@
"resolved": "https://registry.npmjs.org/axios/-/axios-1.16.0.tgz",
"integrity": "sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w==",
"license": "MIT",
- "peer": true,
"dependencies": {
"follow-redirects": "^1.16.0",
"form-data": "^4.0.5",
@@ -7635,7 +7619,6 @@
}
],
"license": "MIT",
- "peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.10.12",
"caniuse-lite": "^1.0.30001782",
@@ -8819,6 +8802,29 @@
"node": ">= 0.8"
}
},
+ "node_modules/encoding": {
+ "version": "0.1.13",
+ "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
+ "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "iconv-lite": "^0.6.2"
+ }
+ },
+ "node_modules/encoding/node_modules/iconv-lite": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/enhanced-resolve": {
"version": "5.20.1",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz",
@@ -10563,7 +10569,6 @@
"integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
"dev": true,
"license": "MIT",
- "peer": true,
"bin": {
"jiti": "bin/jiti.js"
}
@@ -10705,7 +10710,6 @@
"integrity": "sha512-tkuLHQlvWUTeQ3doAqnHbNn8T6WX1KA8yvbKG9x4VtKtIjHsVKQZCH11zRgAfbDAXC2UNIg/K9BYAAcEzUIrNg==",
"dev": true,
"license": "Apache-2.0",
- "peer": true,
"dependencies": {
"copy-anything": "^2.0.1",
"parse-node-version": "^1.0.1",
@@ -12547,7 +12551,6 @@
}
],
"license": "MIT",
- "peer": true,
"dependencies": {
"nanoid": "^3.3.8",
"picocolors": "^1.1.1",
@@ -12955,8 +12958,7 @@
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz",
"integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==",
- "license": "Apache-2.0",
- "peer": true
+ "license": "Apache-2.0"
},
"node_modules/regenerate": {
"version": "1.4.2",
@@ -13372,7 +13374,6 @@
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz",
"integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==",
"license": "Apache-2.0",
- "peer": true,
"dependencies": {
"tslib": "^2.1.0"
}
@@ -14431,7 +14432,6 @@
"integrity": "sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==",
"dev": true,
"license": "BSD-2-Clause",
- "peer": true,
"dependencies": {
"@jridgewell/source-map": "^0.3.3",
"acorn": "^8.8.2",
@@ -14631,8 +14631,7 @@
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
- "license": "0BSD",
- "peer": true
+ "license": "0BSD"
},
"node_modules/tuf-js": {
"version": "3.1.0",
@@ -14688,7 +14687,6 @@
"integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==",
"dev": true,
"license": "Apache-2.0",
- "peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -15094,7 +15092,6 @@
"integrity": "sha512-gX/dMkRQc7QOMzgTe6KsYFM7DxeIONQSui1s0n/0xht36HvrgbxtM1xBlgx596NbpHuQU8P7QpKwrZYwUX48nw==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@types/eslint-scope": "^3.7.7",
"@types/estree": "^1.0.8",
@@ -15174,7 +15171,6 @@
"integrity": "sha512-QcQ72gh8a+7JO63TAx/6XZf/CWhgMzu5m0QirvPfGvptOusAxG12w2+aua1Jkjr7hzaWDnJ2n6JFeexMHI+Zjg==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@types/bonjour": "^3.5.13",
"@types/connect-history-api-fallback": "^1.5.4",
@@ -15770,8 +15766,7 @@
"version": "0.15.1",
"resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.15.1.tgz",
"integrity": "sha512-XE96n56IQpJM7NAoXswY3XRLcWFW83xe0BiAOeMD7K5k5xecOeul3Qcpx6GqEeeHNkW5DWL5zOyTbEfB4eti8w==",
- "license": "MIT",
- "peer": true
+ "license": "MIT"
}
}
}
diff --git a/src/app/app.config.ts b/src/app/app.config.ts
index af498c9..a1c9ba2 100644
--- a/src/app/app.config.ts
+++ b/src/app/app.config.ts
@@ -3,9 +3,10 @@ import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
import { provideIonicAngular } from '@ionic/angular/standalone';
-import {provideHttpClient} from "@angular/common/http";
+import {provideHttpClient, withInterceptors} from "@angular/common/http";
+import {authInterceptor} from "./core/auth/auth.interceptor";
export const appConfig: ApplicationConfig = {
- providers: [provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes), provideIonicAngular({}), provideHttpClient()],
+ providers: [provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes), provideIonicAngular({}),provideHttpClient(withInterceptors([authInterceptor])), provideHttpClient()],
};
diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts
index 0103fa8..59dbf38 100644
--- a/src/app/app.routes.ts
+++ b/src/app/app.routes.ts
@@ -1,4 +1,5 @@
import { Routes } from '@angular/router';
+import {authGuard} from "./core/auth/auth.guard";
export const routes: Routes = [
{
@@ -8,35 +9,29 @@ export const routes: Routes = [
},
{
path: 'main',
+ canActivate: [authGuard],
loadComponent: () => import('./pages/main/main.component').then(x => x.Main),
children: [
- /*{
- path:'groupmessages',
- loadComponent: () => import('./pages/groupmessages/groupmessages.component').then(x => x.Groupmessages)
- },*/
-
{
path: 'messages/:discussionId',
loadComponent: () => import('./pages/messages/messages-main/messages-main.component').then(x => x.MessagesMain)
},
{
- path:'menu',
+ path: 'menu',
loadComponent: () => import('./pages/menu/menu/menu.component').then(x => x.Menu)
},
-
{
- path:'parameters',
+ path: 'parameters',
loadComponent: () => import('./pages/parameters/parameters-main/parameters-main.component').then(x => x.ParametersMain)
}
]
},
{
- path:'login',
+ path: 'login',
loadComponent: () => import('./pages/login-form/login-form.component').then(x => x.LoginFormComponent)
},
-
{
- path:'register',
+ path: 'register',
loadComponent: () => import('./pages/register-form/register-form.component').then(x => x.RegisterFormComponent)
},
];
\ No newline at end of file
diff --git a/src/app/core/auth/auth.guard.ts b/src/app/core/auth/auth.guard.ts
new file mode 100644
index 0000000..cbbf02a
--- /dev/null
+++ b/src/app/core/auth/auth.guard.ts
@@ -0,0 +1,13 @@
+import { inject } from '@angular/core';
+import { CanActivateFn, Router } from '@angular/router';
+import {AuthService} from "./auth.service";
+
+export const authGuard: CanActivateFn = () => {
+ const authService = inject(AuthService);
+ const router = inject(Router);
+
+ if (authService.isLoggedIn()) return true;
+
+ router.navigate(['/login']);
+ return false;
+};
\ No newline at end of file
diff --git a/src/app/core/auth/auth.interceptor.ts b/src/app/core/auth/auth.interceptor.ts
new file mode 100644
index 0000000..192feaf
--- /dev/null
+++ b/src/app/core/auth/auth.interceptor.ts
@@ -0,0 +1,15 @@
+import { HttpInterceptorFn } from '@angular/common/http';
+import { inject } from '@angular/core';
+import {AuthService} from "./auth.service";
+
+export const authInterceptor: HttpInterceptorFn = (req, next) => {
+ const token = inject(AuthService).getToken();
+
+ if (token) {
+ req = req.clone({
+ setHeaders: { Authorization: `Bearer ${token}` }
+ });
+ }
+
+ return next(req);
+};
\ No newline at end of file
diff --git a/src/app/core/auth/auth.service.ts b/src/app/core/auth/auth.service.ts
new file mode 100644
index 0000000..ff61058
--- /dev/null
+++ b/src/app/core/auth/auth.service.ts
@@ -0,0 +1,68 @@
+import { inject, Injectable, signal } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { Router } from '@angular/router';
+import { firstValueFrom } from 'rxjs';
+import {LoggedUser} from "../../models/user.model";
+
+interface LoginResponse {
+ token: string;
+ id: number;
+ username: string;
+ email: string | null;
+ tel: string | null;
+ profilePicture: string | null;
+ description: string | null;
+}
+
+@Injectable({ providedIn: 'root' })
+export class AuthService {
+ private http = inject(HttpClient);
+ private router = inject(Router);
+
+ private readonly TOKEN_KEY = 'knots_token';
+ private readonly USER_KEY = 'knots_user';
+
+ currentUser = signal(this.loadUser());
+
+ private loadUser(): LoggedUser | null {
+ const raw = localStorage.getItem(this.USER_KEY);
+ return raw ? JSON.parse(raw) : null;
+ }
+
+ isLoggedIn(): boolean {
+ return !!localStorage.getItem(this.TOKEN_KEY);
+ }
+
+ getToken(): string | null {
+ return localStorage.getItem(this.TOKEN_KEY);
+ }
+
+ async login(username: string, password: string): Promise {
+ const response = await firstValueFrom(
+ this.http.post('/API/users/login', { username, password })
+ );
+
+ localStorage.setItem(this.TOKEN_KEY, response.token);
+
+ const user: LoggedUser = {
+ id: response.id,
+ username: response.username,
+ email: response.email,
+ tel: response.tel,
+ profilePicture: response.profilePicture,
+ description: response.description,
+ };
+
+ localStorage.setItem(this.USER_KEY, JSON.stringify(user));
+ this.currentUser.set(user);
+
+ await this.router.navigate(['/']); // adapte la route
+ }
+
+ logout(): void {
+ localStorage.removeItem(this.TOKEN_KEY);
+ localStorage.removeItem(this.USER_KEY);
+ this.currentUser.set(null);
+ this.router.navigate(['/login']);
+ }
+}
\ No newline at end of file
diff --git a/src/app/models/user.model.ts b/src/app/models/user.model.ts
new file mode 100644
index 0000000..56cf548
--- /dev/null
+++ b/src/app/models/user.model.ts
@@ -0,0 +1,8 @@
+export interface LoggedUser {
+ id: number;
+ username: string;
+ email: string | null;
+ tel: string | null;
+ profilePicture: string | null;
+ description: string | null;
+}
\ No newline at end of file
diff --git a/src/app/pages/parameters/parameters-coordinates-form/parameters-coordinates-form.component.css b/src/app/pages/parameters/parameters-coordinates-form/parameters-coordinates-form.component.css
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/pages/parameters/parameters-coordinates-form/parameters-coordinates-form.component.html b/src/app/pages/parameters/parameters-coordinates-form/parameters-coordinates-form.component.html
new file mode 100644
index 0000000..555a351
--- /dev/null
+++ b/src/app/pages/parameters/parameters-coordinates-form/parameters-coordinates-form.component.html
@@ -0,0 +1,51 @@
+
+
+ Modifier les coordonnées
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/app/pages/parameters/parameters-coordinates-form/parameters-coordinates-form.component.ts b/src/app/pages/parameters/parameters-coordinates-form/parameters-coordinates-form.component.ts
new file mode 100644
index 0000000..73402cf
--- /dev/null
+++ b/src/app/pages/parameters/parameters-coordinates-form/parameters-coordinates-form.component.ts
@@ -0,0 +1,88 @@
+import {Component, EventEmitter, inject, Output, signal} from '@angular/core';
+import { addIcons } from 'ionicons';
+import { closeOutline } from 'ionicons/icons';
+import {
+ IonButton,
+ IonButtons,
+ IonContent,
+ IonHeader,
+ IonIcon, IonInput,
+ IonItem, IonNote,
+ IonTitle,
+ IonToolbar
+} from "@ionic/angular/standalone";
+import {FormControl, FormGroup, ReactiveFormsModule, Validators} from "@angular/forms";
+import {firstValueFrom} from "rxjs";
+import {KnotsDTOUserUpdateUserContactDto, UsersService} from "../../../services/api";
+
+@Component({
+ selector: 'app-parameters-coordinates-form',
+ imports: [
+ IonHeader,
+ IonToolbar,
+ IonTitle,
+ IonButtons,
+ IonButton,
+ IonIcon,
+ IonContent,
+ ReactiveFormsModule,
+ IonItem,
+ IonInput,
+ IonNote
+ ],
+ templateUrl: './parameters-coordinates-form.component.html',
+ styleUrl: './parameters-coordinates-form.component.css'
+})
+export class ParametersCoordinatesFormComponent {
+ @Output() close = new EventEmitter();
+
+ private userService = inject(UsersService);
+ //private authService = inject(AuthService);
+
+ loading = signal(false);
+
+ coordinatesForm = new FormGroup({
+ email: new FormControl(null, [Validators.required, Validators.email]),
+ tel: new FormControl(null, [Validators.required, Validators.pattern(/^\d{10}$/)]),
+ });
+
+ constructor() {
+ addIcons({ closeOutline });
+ }
+
+
+
+ ngOnInit() {
+ //const user = this.userService.currentUser();
+
+ //this.coordinatesForm.patchValue({
+ //email: user?.email ?? null,
+ //tel: user?.tel ?? null,
+ //});
+ }
+
+ get email() { return this.coordinatesForm.get('email')!; }
+ get tel() { return this.coordinatesForm.get('tel')!; }
+
+ async submitForm() {
+ if (this.coordinatesForm.invalid) return;
+ this.loading.set(true);
+
+ //const user = this.authService.currentUser();
+
+ const userValue: KnotsDTOUserUpdateUserContactDto = {
+ email: this.coordinatesForm.value.email,
+ tel: this.coordinatesForm.value.tel,
+ };
+
+ try {
+ //await firstValueFrom(this.userService.patchUserContactEndpoint(user.id, userValue));
+ this.coordinatesForm.reset();
+ this.close.emit();
+ } catch (e) {
+ console.error('Erreur lors de la mise à jour des coordonnées', e);
+ }
+
+ this.loading.set(false);
+ }
+}
diff --git a/src/app/pages/parameters/parameters-coordinates/parameters-coordinates.component.html b/src/app/pages/parameters/parameters-coordinates/parameters-coordinates.component.html
index b2ef56f..4ddd0c2 100644
--- a/src/app/pages/parameters/parameters-coordinates/parameters-coordinates.component.html
+++ b/src/app/pages/parameters/parameters-coordinates/parameters-coordinates.component.html
@@ -1,4 +1,4 @@
-
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/app/pages/parameters/parameters-coordinates/parameters-coordinates.component.ts b/src/app/pages/parameters/parameters-coordinates/parameters-coordinates.component.ts
index 2099785..961b327 100644
--- a/src/app/pages/parameters/parameters-coordinates/parameters-coordinates.component.ts
+++ b/src/app/pages/parameters/parameters-coordinates/parameters-coordinates.component.ts
@@ -1,43 +1,18 @@
-import {Component, EventEmitter, Input, Output} from '@angular/core';
+import {Component} from '@angular/core';
+import { IonModal, IonButton } from '@ionic/angular/standalone';
import {FormsModule} from "@angular/forms";
-
-export interface Coordonnees {
- phone: string;
- email: string;
-}
+import {ParametersCoordinatesFormComponent} from "../parameters-coordinates-form/parameters-coordinates-form.component";
@Component({
selector: 'app-parameters-coordinates',
- imports: [
- FormsModule
- ],
+ imports: [IonModal, FormsModule, ParametersCoordinatesFormComponent],
templateUrl: './parameters-coordinates.component.html',
styleUrl: './parameters-coordinates.component.css'
})
export class ParametersCoordinatesComponent {
- @Input() isOpen: boolean = false;
- @Output() closed = new EventEmitter();
- @Output() saved = new EventEmitter();
+ isModalOpen = false;
- form: Coordonnees = { phone: '', email: '' };
- phoneFocused = false;
- emailFocused = false;
-
- close(): void {
- this.closed.emit();
- }
-
- save(): void {
- if (this.form.phone || this.form.email) {
- this.saved.emit({ ...this.form });
- this.close();
- }
- }
-
- onOverlayClick(event: MouseEvent): void {
- if ((event.target as HTMLElement).classList.contains('modal-overlay')) {
- this.close();
- }
- }
+ openModal() { this.isModalOpen = true; }
+ closeModal() { this.isModalOpen = false; }
}
diff --git a/src/app/pages/parameters/parameters-profile/parameters-profile.component.css b/src/app/pages/parameters/parameters-profile/parameters-profile.component.css
index 88660c5..b015cad 100644
--- a/src/app/pages/parameters/parameters-profile/parameters-profile.component.css
+++ b/src/app/pages/parameters/parameters-profile/parameters-profile.component.css
@@ -247,4 +247,10 @@
opacity: 1;
transform: scale(1.15);
}
+
+ .field-error {
+ color: var(--ion-color-danger, #eb445a);
+ font-size: 0.75rem;
+ padding: 2px 0 0 4px;
+ }
}
\ No newline at end of file
diff --git a/src/app/pages/parameters/parameters-profile/parameters-profile.component.html b/src/app/pages/parameters/parameters-profile/parameters-profile.component.html
index 61d6c09..ef23de3 100644
--- a/src/app/pages/parameters/parameters-profile/parameters-profile.component.html
+++ b/src/app/pages/parameters/parameters-profile/parameters-profile.component.html
@@ -41,6 +41,9 @@
+
+ {{ usernameError() }}
+
diff --git a/src/app/pages/parameters/parameters-profile/parameters-profile.component.ts b/src/app/pages/parameters/parameters-profile/parameters-profile.component.ts
index 1b46cc9..f1aeb22 100644
--- a/src/app/pages/parameters/parameters-profile/parameters-profile.component.ts
+++ b/src/app/pages/parameters/parameters-profile/parameters-profile.component.ts
@@ -1,6 +1,9 @@
-import {Component, signal} from '@angular/core';
+import {Component, inject, signal} from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
+import {firstValueFrom} from "rxjs";
+import {UsersService} from "../../../services/api";
+import {AuthService} from "../../../core/auth/auth.service";
@Component({
@@ -10,36 +13,118 @@ import { FormsModule } from '@angular/forms';
styleUrl: './parameters-profile.component.css'
})
export class ParametersProfileComponent {
- username = signal('Doggeybag');
- bio = signal('Joueur Valorant');
+ private usersService = inject(UsersService);
+ private authService = inject(AuthService);
+
+ profileImage = signal(null);
+ username = signal(null);
+ bio = signal(null);
editingUsername = signal(false);
editingBio = signal(false);
- profileImage = signal(null);
+ usernameError = signal(null);
+ loading = signal(false);
- onPhotoChange(event: Event): void {
- const input = event.target as HTMLInputElement;
- if (input.files && input.files[0]) {
- const reader = new FileReader();
- reader.onload = (e) => {
- this.profileImage.set(e.target?.result as string);
- };
- reader.readAsDataURL(input.files[0]);
+ ngOnInit() {
+ const user = this.authService.currentUser();
+ this.profileImage.set(user?.profilePicture ?? null);
+ this.username.set(user?.username ?? null);
+ this.bio.set(user?.description ?? null);
+ }
+
+ // --- Username ---
+
+ toggleEditUsername() {
+ if (this.editingUsername()) {
+ this.submitUsername();
+ } else {
+ this.usernameError.set(null);
+ this.editingUsername.set(true);
}
}
- triggerFileInput(): void {
- const input = document.getElementById('photoInput') as HTMLInputElement;
- input?.click();
+ async submitUsername() {
+ const value = this.username()?.trim();
+
+ if (!value) {
+ this.usernameError.set('Le pseudo ne peut pas être vide.');
+ return;
+ }
+ if (value === this.authService.currentUser()?.username) {
+ this.editingUsername.set(false);
+ return;
+ }
+
+ this.loading.set(true);
+ const user = this.authService.currentUser();
+
+ try {
+ await firstValueFrom(this.usersService.patchUsernameEndpoint(user.id, { username: value }));
+ this.usernameError.set(null);
+ this.editingUsername.set(false);
+ } catch (e: any) {
+ if (e?.status === 400) {
+ this.usernameError.set('Ce nom d\'utilisateur est déjà pris.');
+ } else {
+ this.usernameError.set('Erreur lors de la mise à jour.');
+ }
+ }
+
+ this.loading.set(false);
}
- toggleEditUsername(): void {
- this.editingUsername.update((v) => !v);
+ // --- Bio ---
+
+ toggleEditBio() {
+ if (this.editingBio()) {
+ this.submitBio();
+ } else {
+ this.editingBio.set(true);
+ }
}
- toggleEditBio(): void {
- this.editingBio.update((v) => !v);
+ async submitBio() {
+ this.loading.set(true);
+ const user = this.authService.currentUser();
+
+ try {
+ await firstValueFrom(this.usersService.patchUserDescriptionEndpoint(user.id, { description: this.bio() }));
+ this.editingBio.set(false);
+ } catch (e) {
+ console.error('Erreur lors de la mise à jour de la bio', e);
+ }
+
+ this.loading.set(false);
+ }
+
+ // --- Photo ---
+
+ triggerFileInput() {
+ document.getElementById('photoInput')?.click();
+ }
+
+ async onPhotoChange(event: Event) {
+ const file = (event.target as HTMLInputElement).files?.[0];
+ if (!file) return;
+
+ const reader = new FileReader();
+ reader.onload = async () => {
+ const base64 = reader.result as string;
+ this.profileImage.set(base64);
+
+ this.loading.set(true);
+ const user = this.authService.currentUser();
+
+ try {
+ await firstValueFrom(this.usersService.patchUserProfilePictureEndpoint(user.id, { profilePicture: base64 }));
+ } catch (e) {
+ console.error('Erreur lors de la mise à jour de la photo', e);
+ }
+
+ this.loading.set(false);
+ };
+ reader.readAsDataURL(file);
}
}
diff --git a/src/app/services/api/api/discussions.service.ts b/src/app/services/api/api/discussions.service.ts
index 45e4bac..aacc2be 100644
--- a/src/app/services/api/api/discussions.service.ts
+++ b/src/app/services/api/api/discussions.service.ts
@@ -172,17 +172,29 @@ export class DiscussionsService extends BaseService {
/**
* @endpoint get /API/discussions
* @param id
+ * @param name
+ * @param isGroup
+ * @param membersCount
* @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 getDiscussionEndpoint(id: number, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable;
- public getDiscussionEndpoint(id: number, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable>;
- public getDiscussionEndpoint(id: number, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable>;
- public getDiscussionEndpoint(id: number, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable {
+ public getDiscussionEndpoint(id: number, name: string, isGroup: boolean, membersCount: number, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable;
+ public getDiscussionEndpoint(id: number, name: string, isGroup: boolean, membersCount: number, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable>;
+ public getDiscussionEndpoint(id: number, name: string, isGroup: boolean, membersCount: number, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable>;
+ public getDiscussionEndpoint(id: number, name: string, isGroup: boolean, membersCount: number, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable {
if (id === null || id === undefined) {
throw new Error('Required parameter id was null or undefined when calling getDiscussionEndpoint.');
}
+ if (name === null || name === undefined) {
+ throw new Error('Required parameter name was null or undefined when calling getDiscussionEndpoint.');
+ }
+ if (isGroup === null || isGroup === undefined) {
+ throw new Error('Required parameter isGroup was null or undefined when calling getDiscussionEndpoint.');
+ }
+ if (membersCount === null || membersCount === undefined) {
+ throw new Error('Required parameter membersCount was null or undefined when calling getDiscussionEndpoint.');
+ }
let localVarQueryParameters = new OpenApiHttpParams(this.encoder);
@@ -195,6 +207,33 @@ export class DiscussionsService extends BaseService {
);
+ localVarQueryParameters = this.addToHttpParams(
+ localVarQueryParameters,
+ 'Name',
+ name,
+ QueryParamStyle.Form,
+ true,
+ );
+
+
+ localVarQueryParameters = this.addToHttpParams(
+ localVarQueryParameters,
+ 'IsGroup',
+ isGroup,
+ QueryParamStyle.Form,
+ true,
+ );
+
+
+ localVarQueryParameters = this.addToHttpParams(
+ localVarQueryParameters,
+ 'MembersCount',
+ membersCount,
+ QueryParamStyle.Form,
+ true,
+ );
+
+
let localVarHeaders = this.defaultHeaders;
const localVarHttpHeaderAcceptSelected: string | undefined = options?.httpHeaderAccept ?? this.configuration.selectHeaderAccept([
diff --git a/src/app/services/api/api/users.service.ts b/src/app/services/api/api/users.service.ts
index 16b6eba..6ebc6cd 100644
--- a/src/app/services/api/api/users.service.ts
+++ b/src/app/services/api/api/users.service.ts
@@ -58,10 +58,10 @@ export class UsersService extends BaseService {
* @param reportProgress flag to report request and response progress.
* @param options additional options
*/
- public createUserEndpoint(knotsDTOUserCreateUserDto: KnotsDTOUserCreateUserDto, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable;
- public createUserEndpoint(knotsDTOUserCreateUserDto: KnotsDTOUserCreateUserDto, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>;
- public createUserEndpoint(knotsDTOUserCreateUserDto: KnotsDTOUserCreateUserDto, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>;
- public createUserEndpoint(knotsDTOUserCreateUserDto: KnotsDTOUserCreateUserDto, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable {
+ public createUserEndpoint(knotsDTOUserCreateUserDto: KnotsDTOUserCreateUserDto, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable;
+ public createUserEndpoint(knotsDTOUserCreateUserDto: KnotsDTOUserCreateUserDto, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public createUserEndpoint(knotsDTOUserCreateUserDto: KnotsDTOUserCreateUserDto, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>;
+ public createUserEndpoint(knotsDTOUserCreateUserDto: KnotsDTOUserCreateUserDto, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable {
if (knotsDTOUserCreateUserDto === null || knotsDTOUserCreateUserDto === undefined) {
throw new Error('Required parameter knotsDTOUserCreateUserDto was null or undefined when calling createUserEndpoint.');
}
@@ -69,8 +69,7 @@ export class UsersService extends BaseService {
let localVarHeaders = this.defaultHeaders;
const localVarHttpHeaderAcceptSelected: string | undefined = options?.httpHeaderAccept ?? this.configuration.selectHeaderAccept([
- 'application/json',
- 'application/problem+json'
+ 'application/json'
]);
if (localVarHttpHeaderAcceptSelected !== undefined) {
localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
diff --git a/src/app/services/api/chat.service.ts b/src/app/services/api/chat.service.ts
deleted file mode 100644
index a4dd04d..0000000
--- a/src/app/services/api/chat.service.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-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);
- }
-}
\ No newline at end of file
diff --git a/src/app/services/api/discussion.service.ts b/src/app/services/api/discussion.service.ts
deleted file mode 100644
index 4c14d21..0000000
--- a/src/app/services/api/discussion.service.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-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 {
- return this.http.get(`${this.apiUrl}/discussions`);
- }
-}
\ No newline at end of file
diff --git a/src/app/services/api/login.service.ts b/src/app/services/api/login.service.ts
deleted file mode 100644
index 44fd835..0000000
--- a/src/app/services/api/login.service.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-import { Injectable, inject } from '@angular/core';
-import { HttpClient } from "@angular/common/http";
-import { Observable, tap, finalize } from "rxjs";
-
-export interface LoginRequest {
- name: string;
- password: string;
-}
-
-export interface LoginResponse {
- token: string;
- discussionId: string;
-}
-
-@Injectable({
- providedIn: 'root'
-})
-export class LoginService {
-
- private http = inject(HttpClient);
- private apiUrl = 'https://localhost:5001/API';
- private isRefreshing = false;
-
- login(credentials: LoginRequest): Observable {
- return this.http.post(`${this.apiUrl}/auth/login`, credentials).pipe(
- tap(response => localStorage.setItem('token', response.token))
- );
- }
-
- refreshToken(): Observable<{ token: string }> {
- this.isRefreshing = true;
-
- return this.http.post<{ token: string }>(`${this.apiUrl}/auth/refresh`, {}).pipe(
- tap(response => localStorage.setItem('token', response.token)),
- finalize(() => { this.isRefreshing = false; })
- );
- }
-
- logout() {
- localStorage.removeItem('token');
- }
-}
\ No newline at end of file
diff --git a/src/app/services/api/model/models.ts b/src/app/services/api/model/models.ts
index 54e4581..a9690cf 100644
--- a/src/app/services/api/model/models.ts
+++ b/src/app/services/api/model/models.ts
@@ -19,4 +19,4 @@ export * from './knots-dto-user-update-user-contact-dto';
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-dto-user-update-username-dto';
\ No newline at end of file