Compare commits

..

19 Commits

Author SHA1 Message Date
sanchezvem 14637d415a rebuild android app 2026-06-09 09:25:49 +01:00
sanchezvem c8f46f6c8f Changed method to see pictures 2026-06-05 11:08:28 +01:00
sanchezvem ed3d9bf52e cleaned code 2026-05-18 16:35:12 +01:00
sanchezvem b3cae6e3a9 Change empty box 2026-05-18 16:34:26 +01:00
sanchezvem a37b45f5b6 fixed error message on auth 2026-05-17 16:10:50 +01:00
sanchezvem 036ba39a5f Added permissions 2026-05-17 15:47:02 +01:00
sanchezvem ac9d62d284 Cleaned code 2026-05-14 18:49:56 +01:00
sanchezvem 8f836c6d80 Changed variables name 2026-05-14 18:35:43 +01:00
sanchezvem e8c308adf8 Changed errors messages 2026-05-14 14:37:44 +01:00
sanchezvem 42116be9a6 Added segments to separe friends and global 2026-05-14 14:07:37 +01:00
sanchezvem eee7958bce Added posts and possibility to like post 2026-05-14 13:55:00 +01:00
sanchezvem d5584ebf0a Fixed error in hour of challenge accomplished 2026-05-14 11:49:46 +01:00
sanchezvem 6a1ed5abeb Started implementation of all posts 2026-05-14 11:19:48 +01:00
sanchezvem e483bc4e57 Cleaned code 2026-05-13 23:06:30 +01:00
sanchezvem f1354c2db3 added functions and web-socket to chat in group 2026-05-13 23:01:25 +01:00
sanchezvem bd0d15bbf0 added functions and web-socket to chat in group 2026-05-13 23:01:13 +01:00
sanchezvem 90749ea55e added form into vue to send message 2026-05-13 20:47:55 +01:00
sanchezvem ff7039a80b added messages 2026-05-13 19:52:25 +01:00
sanchezvem fb467d52d9 changed groups page to publication page 2026-05-13 18:13:01 +01:00
50 changed files with 593 additions and 276 deletions
+2
View File
@@ -38,4 +38,6 @@
<!-- Permissions --> <!-- Permissions -->
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
</manifest> </manifest>
Binary file not shown.
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.0-bin.zip
networkTimeout=10000 networkTimeout=10000
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
+1 -4
View File
@@ -1,7 +1,7 @@
#!/bin/sh #!/bin/sh
# #
# Copyright © 2015-2021 the original authors. # Copyright © 2015 the original authors.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@@ -114,7 +114,6 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;; NONSTOP* ) nonstop=true ;;
esac esac
CLASSPATH="\\\"\\\""
# Determine the Java command to use to start the JVM. # Determine the Java command to use to start the JVM.
@@ -172,7 +171,6 @@ fi
# For Cygwin or MSYS, switch paths to Windows format before running java # For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" ) JAVACMD=$( cygpath --unix "$JAVACMD" )
@@ -212,7 +210,6 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
set -- \ set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \ "-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@" "$@"
+1 -2
View File
@@ -70,11 +70,10 @@ goto fail
:execute :execute
@rem Setup the command line @rem Setup the command line
set CLASSPATH=
@rem Execute Gradle @rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end :end
@rem End local scope for the variables with windows NT shell @rem End local scope for the variables with windows NT shell
+10
View File
@@ -22,6 +22,7 @@
"@openapitools/openapi-generator-cli": "^2.30.2", "@openapitools/openapi-generator-cli": "^2.30.2",
"@tailwindcss/postcss": "^4.2.1", "@tailwindcss/postcss": "^4.2.1",
"browser-image-compression": "^2.0.2", "browser-image-compression": "^2.0.2",
"jwt-decode": "^4.0.0",
"moment": "^2.30.1", "moment": "^2.30.1",
"postcss": "^8.5.8", "postcss": "^8.5.8",
"rimraf": "^6.1.3", "rimraf": "^6.1.3",
@@ -7158,6 +7159,15 @@
], ],
"license": "MIT" "license": "MIT"
}, },
"node_modules/jwt-decode": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz",
"integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==",
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/kleur": { "node_modules/kleur": {
"version": "4.1.5", "version": "4.1.5",
"resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
+1
View File
@@ -36,6 +36,7 @@
"@openapitools/openapi-generator-cli": "^2.30.2", "@openapitools/openapi-generator-cli": "^2.30.2",
"@tailwindcss/postcss": "^4.2.1", "@tailwindcss/postcss": "^4.2.1",
"browser-image-compression": "^2.0.2", "browser-image-compression": "^2.0.2",
"jwt-decode": "^4.0.0",
"moment": "^2.30.1", "moment": "^2.30.1",
"postcss": "^8.5.8", "postcss": "^8.5.8",
"rimraf": "^6.1.3", "rimraf": "^6.1.3",
+1 -1
View File
@@ -14,7 +14,7 @@ export const routes: Routes = [
}, },
{ {
path: 'groups', path: 'groups',
loadComponent: () => import('./pages/groups/groups.component').then(m => m.GroupsComponent) loadComponent: () => import('./pages/publication/publication.component').then(m => m.PublicationComponent)
}, },
{ {
path: 'ranking', path: 'ranking',
@@ -3,11 +3,11 @@
<div class="grid grid-cols-5 items-center"> <div class="grid grid-cols-5 items-center">
<h3 class="col-span-4 text-base font-semibold text-gray-900 m-0"> <h3 class="col-span-4 text-base font-semibold text-gray-900 m-0">
{{ data().label }} {{ challenge().label }}
</h3> </h3>
</div> </div>
<p class="text-[11px] text-gray-500 leading-relaxed">{{ data().libelle }}</p> <p class="text-[11px] text-gray-500 leading-relaxed">{{ challenge().libelle }}</p>
<div class="grid grid-cols-5 items-center"> <div class="grid grid-cols-5 items-center">
<span class="col-span-4 text-[11px] text-gray-500"> <span class="col-span-4 text-[11px] text-gray-500">
@@ -41,11 +41,11 @@
<div class="mt-4 bg-white rounded-2xl shadow-sm p-4 space-y-3"> <div class="mt-4 bg-white rounded-2xl shadow-sm p-4 space-y-3">
<div class="grid grid-cols-5 items-center"> <div class="grid grid-cols-5 items-center">
<h3 class="col-span-4 text-base font-semibold text-gray-900 m-0"> <h3 class="col-span-4 text-base font-semibold text-gray-900 m-0">
{{ data().label }} {{ challenge().label }}
</h3> </h3>
</div> </div>
<p class="text-[11px] text-gray-500 leading-relaxed">{{ data().libelle }}</p> <p class="text-[11px] text-gray-500 leading-relaxed">{{ challenge().libelle }}</p>
</div> </div>
<app-tooltip title="Aucun malus si non réalisé" <app-tooltip title="Aucun malus si non réalisé"
@@ -55,7 +55,7 @@
<app-proof-form #proofForm></app-proof-form> <app-proof-form #proofForm></app-proof-form>
</div> </div>
<ion-button expand="block" (click)="sendProof(data().id)">Soumettre ma preuve</ion-button> <ion-button expand="block" (click)="sendProof(challenge().id)">Soumettre ma preuve</ion-button>
</ion-content> </ion-content>
</ng-template> </ng-template>
@@ -1,10 +1,15 @@
import {Component, inject, input, signal, viewChild} from '@angular/core'; import {Component, inject, input, viewChild} from '@angular/core';
import {IonicModule, LoadingController, ToastController} from "@ionic/angular"; import {IonicModule, LoadingController, ToastController} from "@ionic/angular";
import {GetRandomChallengeDto, RandomchallengesService} from "../../services/api"; import {GetRandomChallengeDto, RandomchallengesService} from "../../services/api";
import {firstValueFrom} from "rxjs"; import {firstValueFrom} from "rxjs";
import {TooltipComponent} from "../tooltip/tooltip.component"; import {TooltipComponent} from "../tooltip/tooltip.component";
import {ProofFormComponent} from "../proof-form/proof-form.component"; import {ProofFormComponent} from "../proof-form/proof-form.component";
import {SignOnFormComponent} from "../sign-on-form/sign-on-form.component"; import {addIcons} from "ionicons";
import {checkmarkCircleOutline} from "ionicons/icons";
addIcons({
"checkmark-circle": checkmarkCircleOutline,
})
@Component({ @Component({
selector: 'app-challenge-card', selector: 'app-challenge-card',
@@ -21,8 +26,7 @@ export class ChallengeCardComponent {
private toastCtrl = inject(ToastController); private toastCtrl = inject(ToastController);
private loadCtrl = inject(LoadingController); private loadCtrl = inject(LoadingController);
data = input.required<GetRandomChallengeDto>(); challenge = input.required<GetRandomChallengeDto>();
proof = viewChild<ProofFormComponent>('proofForm'); proof = viewChild<ProofFormComponent>('proofForm');
isModalOpen = false; isModalOpen = false;
@@ -60,10 +64,10 @@ export class ChallengeCardComponent {
color: 'success' color: 'success'
}); });
await toast.present(); await toast.present();
} catch { } catch (e) {
this.isModalOpen = false; this.isModalOpen = false;
const toast = await this.toastCtrl.create({ const toast = await this.toastCtrl.create({
message: 'Tu as déjà déposé une preuve pour ce défi', message: e.error ?? "Impossible de déposer une preuve",
duration: 2000, duration: 2000,
color: 'danger' color: 'danger'
}); });
@@ -8,7 +8,6 @@
<p class="m-0 mt-1 text-xs text-gray-500">{{ challenge.challengeDescription }}</p> <p class="m-0 mt-1 text-xs text-gray-500">{{ challenge.challengeDescription }}</p>
</div> </div>
<div class="text-right shrink-0"> <div class="text-right shrink-0">
<p class="m-0 text-xs text-gray-500">{{ converterHours(challenge.challengeDuration) }}</p>
<p class="m-0 mt-1 text-xs text-gray-400">{{ challenge.challengeStartDate }}</p> <p class="m-0 mt-1 text-xs text-gray-400">{{ challenge.challengeStartDate }}</p>
</div> </div>
</div> </div>
@@ -12,28 +12,4 @@ import {GetUserChallengeDto} from "../../services/api";
}) })
export class ChallengesAccomplishedComponent { export class ChallengesAccomplishedComponent {
userChallenges = input.required<GetUserChallengeDto[]>(); userChallenges = input.required<GetUserChallengeDto[]>();
converterHours(hours: number) {
const day = Math.floor(hours / 24);
const week = Math.floor(day / 7);
const month = Math.floor(week / 4);
const year = Math.floor(month / 12);
switch (true) {
case year > 0:
return `${year} an${year > 1 ? 's' : ''}`;
case month > 0:
return `${month} mois`;
case week > 0:
return `${week} semaine${week > 1 ? 's' : ''}`;
case day > 0:
return `${day} jour${day > 1 ? 's' : ''}`;
default:
return `${hours} heure${hours > 1 ? 's' : ''}`;
}
}
} }
@@ -1,5 +1,5 @@
@if (friendsRequest().length > 0) { @if (friendsRequest().length) {
<div class="rounded-xl px-5 m-3 bg-white overflow-auto font-mono border-1 border-gray-300"> <div class="rounded-xl px-5 m-3 bg-white overflow-auto font-mono border border-gray-300">
<ion-list> <ion-list>
@for (request of friendsRequest(); track request.userId; let i = $index) { @for (request of friendsRequest(); track request.userId; let i = $index) {
@if (i == friendsRequest().length - 1) { @if (i == friendsRequest().length - 1) {
@@ -37,9 +37,15 @@
</ion-list> </ion-list>
</div> </div>
} @else { } @else {
<div class="flex justify-center items-center p-4"> <ion-item lines="none" class="border border-stone-200 rounded-xl m-3" style="--background: #fafaf8;">
<p class="text-center text-sm italic text-gray-500 font-serif"> <div class="flex flex-col items-center w-full px-10 py-20 gap-3">
Vous n'avez aucune demande d'ami <div class="w-10 h-10 rounded-full bg-stone-100 border border-stone-200 flex items-center justify-center">
</p> <ion-icon name="people-outline" style="color:#a8a090; font-size:20px;"></ion-icon>
</div> </div>
<div class="text-center">
<p class="m-0 text-sm font-medium text-stone-400">Ajoutez vos amis</p>
<p class="m-0 mt-1 text-xs text-stone-300 leading-relaxed">Vos demandes d'amis apparaîtront ici</p>
</div>
</div>
</ion-item>
} }
@@ -1,6 +1,6 @@
import {Component, inject} from '@angular/core'; import {Component, inject} from '@angular/core';
import {IonicModule, LoadingController, ToastController} from "@ionic/angular"; import {IonicModule, LoadingController, ToastController} from "@ionic/angular";
import {checkmarkCircleOutline, closeCircleOutline} from 'ionicons/icons'; import {checkmarkCircleOutline, closeCircleOutline, peopleOutline} from 'ionicons/icons';
import {addIcons} from "ionicons"; import {addIcons} from "ionicons";
import {PipeComponent} from "../pipe/pipe.component"; import {PipeComponent} from "../pipe/pipe.component";
import {FriendsService, GetFriendRequestDto} from "../../services/api"; import {FriendsService, GetFriendRequestDto} from "../../services/api";
@@ -9,7 +9,8 @@ import {FriendsStateService} from "../../services/friends-state";
addIcons({ addIcons({
'check': checkmarkCircleOutline, 'check': checkmarkCircleOutline,
'close': closeCircleOutline 'close': closeCircleOutline,
'people-outline': peopleOutline
}); });
@Component({ @Component({
@@ -1,5 +1,5 @@
@if (friends().length) { @if (friends().length) {
<div class="rounded-xl px-5 m-3 bg-white font-mono border-1 border-gray-300"> <div class="rounded-xl px-5 m-3 bg-white font-mono border border-gray-300">
<ion-list> <ion-list>
@for (friend of friends(); track friend.friendId; let i = $index) { @for (friend of friends(); track friend.friendId; let i = $index) {
@if (i == friends().length - 1) { @if (i == friends().length - 1) {
@@ -35,11 +35,17 @@
</ion-list> </ion-list>
</div> </div>
} @else { } @else {
<div class="flex justify-center items-center p-4"> <ion-item lines="none" class="border border-stone-200 rounded-xl m-3" style="--background: #fafaf8;">
<p class="text-center text-sm italic text-gray-500 font-serif"> <div class="flex flex-col items-center w-full px-10 py-20 gap-3">
C'est plus marrant à plusieurs, ajoute des amis ! <div class="w-10 h-10 rounded-full bg-stone-100 border border-stone-200 flex items-center justify-center">
</p> <ion-icon name="people" style="color:#a8a090; font-size:20px;"></ion-icon>
</div> </div>
<div class="text-center">
<p class="m-0 text-sm font-medium text-stone-400">Ajoutez vos amis</p>
<p class="m-0 mt-1 text-xs text-stone-300 leading-relaxed">Vos amis apparaîtront ici</p>
</div>
</div>
</ion-item>
} }
<ion-modal [isOpen]="isModalOpen"> <ion-modal [isOpen]="isModalOpen">
@@ -1,6 +1,6 @@
import {Component, inject, OnInit, signal} from '@angular/core'; import {Component, inject, signal} from '@angular/core';
import {IonicModule, LoadingController, ToastController} from "@ionic/angular"; import {IonicModule, LoadingController, ToastController} from "@ionic/angular";
import {closeCircleOutline} from 'ionicons/icons'; import {closeCircleOutline, peopleOutline} from 'ionicons/icons';
import {addIcons} from "ionicons"; import {addIcons} from "ionicons";
import {FriendsService, GetUserDto, UsersService} from "../../services/api"; import {FriendsService, GetUserDto, UsersService} from "../../services/api";
import {firstValueFrom} from "rxjs"; import {firstValueFrom} from "rxjs";
@@ -9,6 +9,7 @@ import {GenericUserInfoComponent} from "../generic-user-info/generic-user-info.c
addIcons({ addIcons({
'close': closeCircleOutline, 'close': closeCircleOutline,
'people': peopleOutline
}); });
@Component({ @Component({
@@ -3,7 +3,7 @@
<div class="bg-white rounded-lg p-2 shadow-sm border border-gray-200 overflow-scroll max-h-full"> <div class="bg-white rounded-lg p-2 shadow-sm border border-gray-200 overflow-scroll max-h-full">
<div class="grid grid-cols-4 gap-3"> <div class="grid grid-cols-4 gap-3">
@for (p of proofs(); track p) { @for (p of proofs(); track p) {
<img [src]="'data:image/jpeg;base64,' + p.proof" <img [src]="p.proof"
class="w-20 h-20 object-cover" class="w-20 h-20 object-cover"
alt="" alt=""
(click)="openProof(p.proof)" (click)="openProof(p.proof)"
@@ -18,7 +18,7 @@
<ion-icon name="close-circle-outline"></ion-icon> <ion-icon name="close-circle-outline"></ion-icon>
</button> </button>
<img [src]="'data:image/jpeg;base64,' + selectedProof()" <img [src]="selectedProof()"
class="w-[90%] h-[90%] object-cover rounded-md mt-10" class="w-[90%] h-[90%] object-cover rounded-md mt-10"
alt="" alt=""
/> />
@@ -1,3 +0,0 @@
<p>
group-chat works!
</p>
@@ -1,16 +0,0 @@
import {Component, OnInit} from '@angular/core';
@Component({
selector: 'app-group-chat',
templateUrl: './group-chat.component.html',
styleUrls: ['./group-chat.component.scss'],
})
export class GroupChatComponent implements OnInit {
constructor() {
}
ngOnInit() {
}
}
@@ -113,8 +113,8 @@ export class GroupInfoComponent implements OnInit {
const oldGroup = this.group().id; const oldGroup = this.group().id;
try { try {
await firstValueFrom(this.groupsService.deleteGroupEndpoint(oldGroup)); await firstValueFrom(this.groupsService.deleteGroupEndpoint(oldGroup));
this.idGroup.emit(oldGroup); this.idGroup.emit(oldGroup);
const toast = await this.toastCtrl.create({ const toast = await this.toastCtrl.create({
message: 'Groupe supprimé', message: 'Groupe supprimé',
duration: 2000, duration: 2000,
@@ -1,3 +1,8 @@
<p> <form [formGroup]="messageForm">
message-form works! <ion-item lines="none" class="rounded-2xl border border-stone-200 mt-2">
</p> <ion-input placeholder="Envoyer un message..." formControlName="libelle"></ion-input>
<ion-button style="--background: white" (click)="sendMessageOnGroup()">
<ion-icon name="send-outline" class="text-xl text-stone-400"></ion-icon>
</ion-button>
</ion-item>
</form>
@@ -1,16 +1,66 @@
import {Component, OnInit} from '@angular/core'; import {Component, inject, input} from '@angular/core';
import {IonicModule, LoadingController, ToastController} from "@ionic/angular";
import {FormControl, FormGroup, ReactiveFormsModule, Validators} from "@angular/forms";
import {addIcons} from "ionicons";
import {sendOutline} from 'ionicons/icons';
import {GetGroupDetailsDto, MessagesService} from "../../services/api";
import {firstValueFrom} from "rxjs";
addIcons({
"send-outline": sendOutline
})
@Component({ @Component({
selector: 'app-message-form', selector: 'app-message-form',
templateUrl: './message-form.component.html', templateUrl: './message-form.component.html',
styleUrls: ['./message-form.component.scss'], styleUrls: ['./message-form.component.scss'],
imports: [
IonicModule,
ReactiveFormsModule
]
}) })
export class MessageFormComponent implements OnInit { export class MessageFormComponent {
private messagesService = inject(MessagesService);
private loadCtrl = inject(LoadingController);
private toastCtrl = inject(ToastController);
constructor() { groupId = input.required<GetGroupDetailsDto["id"]>();
messageForm: FormGroup = new FormGroup({
libelle: new FormControl<string>(null, [Validators.required]),
})
async sendMessageOnGroup() {
const loading = await this.loadCtrl.create({
message: 'Envoi...',
spinner: 'lines-sharp-small'
});
await loading.present();
if (this.messageForm.invalid) {
this.messageForm.reset();
const toast = await this.toastCtrl.create({
message: 'Message incorrect',
duration: 2000,
color: 'danger'
});
await toast.present();
return;
}
try {
await firstValueFrom(this.messagesService.sendMessageEndpoint(this.groupId(), this.messageForm.getRawValue()));
this.messageForm.reset();
} catch {
const toast = await this.toastCtrl.create({
message: 'Impossible d\'envoyer le messages.',
duration: 2000,
color: 'danger'
});
await toast.present();
this.messageForm.reset();
}
await loading.dismiss();
} }
ngOnInit() {
}
} }
@@ -1,3 +1,24 @@
<p> @if (messages().length) {
message works! <div class="overflow-scroll h-11/12">
</p> <app-messages-list [messages]="messages()"></app-messages-list>
</div>
} @else {
<ion-item lines="none" class="mt-[60%]" style="--background: #F7F6F2;">
<div class="flex flex-col items-center w-full gap-3">
<div class="relative w-14 h-14">
<div class="w-14 h-14 rounded-full bg-white border border-stone-200 flex items-center justify-center">
<ion-icon name="chatbubble-ellipses-outline" class="text-2xl text-stone-400"></ion-icon>
</div>
<div class="absolute -bottom-0.5 -right-0.5 w-5 h-5 rounded-full bg-white border border-stone-200 flex items-center justify-center">
<ion-icon name="moon-outline" class="text-[10px] text-stone-400"></ion-icon>
</div>
</div>
<div class="text-center flex flex-col gap-1">
<p class="m-0 text-sm font-medium text-stone-400">C'est bien calme par ici</p>
<p class="m-0 text-xs text-stone-300 leading-relaxed">Les messages du groupe apparaîtront ici</p>
</div>
</div>
</ion-item>
}
<app-message-form [groupId]="groupId()"></app-message-form>
@@ -1,16 +1,60 @@
import {Component, OnInit} from '@angular/core'; import {Component, inject, input, OnDestroy, OnInit} from '@angular/core';
import {GetGroupDetailsDto, MessagesService} from "../../services/api";
import {firstValueFrom} from "rxjs";
import {IonicModule, LoadingController, ToastController} from "@ionic/angular";
import {addIcons} from "ionicons";
import {chatbubbleEllipsesOutline, moonOutline} from 'ionicons/icons';
import {ChatService} from "../../services/chat-service";
import {MessagesListComponent} from "../messages-list/messages-list.component";
import {MessageFormComponent} from "../message-form/message-form.component";
addIcons({
"chatbubble-ellipses-outline": chatbubbleEllipsesOutline,
"moon-outline": moonOutline,
})
@Component({ @Component({
selector: 'app-message', selector: 'app-message',
templateUrl: './message.component.html', templateUrl: './message.component.html',
styleUrls: ['./message.component.scss'], styleUrls: ['./message.component.scss'],
imports: [
IonicModule,
MessagesListComponent,
MessageFormComponent
]
}) })
export class MessageComponent implements OnInit { export class MessageComponent implements OnInit, OnDestroy {
private messagesService = inject(MessagesService);
private loadCtrl = inject(LoadingController);
private toastCtrl = inject(ToastController);
private chatService = inject(ChatService);
constructor() { messages = this.chatService.messages;
groupId = input.required<GetGroupDetailsDto["id"]>();
async ngOnInit() {
const loading = await this.loadCtrl.create({
message: 'Chargement...',
spinner: 'lines-sharp-small'
});
await loading.present();
try {
const messages = await firstValueFrom(this.messagesService.getMessagesEndpoint(this.groupId()));
this.chatService.setMessages(messages);
await this.chatService.connect(this.groupId());
} catch {
const toast = await this.toastCtrl.create({
message: 'Impossible de charger les messages.',
duration: 2000,
color: 'danger'
});
await toast.present();
}
await loading.dismiss();
} }
ngOnInit() { async ngOnDestroy() {
await this.chatService.disconnect();
} }
} }
@@ -0,0 +1,36 @@
<div class="m-3">
<app-title-part textInfo="Début de la conversation"></app-title-part>
</div>
@for (message of messages(); track message.id) {
@if (message.userId === currentUser) {
<div class="flex flex-row-reverse items-end">
<div class="flex flex-col items-end">
<div class="px-3.5 py-2 text-sm rounded-3xl rounded-br-sm bg-violet-700 text-white max-w-xs">
{{ message.libelle }}
</div>
<span class="text-[10px] mt-0.5 mr-1 mb-0.5">
{{ message.sendDate | date:'HH:mm' }}
</span>
</div>
</div>
} @else {
<div class="flex items-end gap-2">
<div class="w-8 h-8 rounded-full flex items-center justify-center text-xs mb-2 bg-teal-100 text-teal-700 max-w-xs">
{{ message.username.substring(0, 2) }}
</div>
<div class="flex flex-col">
<span class="text-[11px] mb-1 ml-1">
{{ message.username }}
</span>
<div class="px-3.5 py-2 text-sm rounded-3xl rounded-bl-sm border">
{{ message.libelle }}
</div>
<span class="text-[10px] mt-0.5 mx-1 mb-0.5">
{{ message.sendDate | date:'HH:mm' }}
</span>
</div>
</div>
}
}
@@ -0,0 +1,24 @@
import {Component, inject, input} from '@angular/core';
import {DatePipe} from "@angular/common";
import {IonicModule} from "@ionic/angular";
import {GetMessageDto} from "../../services/api";
import {jwtDecode} from "jwt-decode";
import {AuthManageService} from "../../services/auth-manage";
import {TitlePartComponent} from "../title-part/title-part.component";
@Component({
selector: 'app-messages-list',
templateUrl: './messages-list.component.html',
styleUrls: ['./messages-list.component.scss'],
imports: [
DatePipe,
IonicModule,
TitlePartComponent
]
})
export class MessagesListComponent {
private authService = inject(AuthManageService);
messages = input.required<GetMessageDto[]>();
currentUser = Number(jwtDecode<any>(this.authService.getToken()).UserId);
}
@@ -10,7 +10,7 @@
</ion-tab-button> </ion-tab-button>
<ion-tab-button tab="groups" routerLink="/groups"> <ion-tab-button tab="groups" routerLink="/groups">
<ion-icon name="chatbubbles-outline"></ion-icon> <ion-icon name="globe-outline"></ion-icon>
</ion-tab-button> </ion-tab-button>
<ion-tab-button tab="ranking" routerLink="/ranking"> <ion-tab-button tab="ranking" routerLink="/ranking">
@@ -1,12 +1,12 @@
import {Component} from '@angular/core'; import {Component} from '@angular/core';
import {IonicModule} from "@ionic/angular"; import {IonicModule} from "@ionic/angular";
import {addIcons} from 'ionicons'; import {addIcons} from 'ionicons';
import {homeOutline, peopleOutline, chatbubblesOutline, statsChartOutline} from 'ionicons/icons'; import {homeOutline, peopleOutline, globeOutline, statsChartOutline} from 'ionicons/icons';
addIcons({ addIcons({
'home-outline': homeOutline, 'home-outline': homeOutline,
'people-outline': peopleOutline, 'people-outline': peopleOutline,
'chatbubbles-outline': chatbubblesOutline, 'globe-outline': globeOutline,
'stats-chart-outline': statsChartOutline 'stats-chart-outline': statsChartOutline
}); });
@@ -0,0 +1,80 @@
<ion-segment>
<ion-segment-button value="first" content-id="global">
<ion-label>Général</ion-label>
</ion-segment-button>
<ion-segment-button value="second" content-id="friends">
<ion-label>Amis</ion-label>
</ion-segment-button>
</ion-segment>
<ion-segment-view>
<ion-segment-content id="global">
@for (post of localPosts(); track post.id) {
<ion-item lines="full">
<div class="mt-2">
<div class="flex items-center gap-2 p-3">
<div class="w-8 h-8 rounded-full flex items-center justify-center text-xs bg-teal-100 text-teal-700 max-w-xs">
{{ post.username.substring(0, 2) }}
</div>
<span class="text-lg font-mono ml-2">{{ post.username }}</span>
</div>
<div class="aspect-square overflow-hidden p-1">
<img [src]="post.proof"
alt=""
class="w-full h-full object-cover"/>
</div>
<div class="px-3 py-3">
<div class="flex items-center gap-2 mb-1">
<button class="flex items-center p-0 bg-transparent border-none cursor-pointer"
(click)="likePost(post.id)">
@if (post.isLiked) {
<ion-icon name="heart" class="text-red-700 text-2xl"></ion-icon>
} @else {
<ion-icon name="heart-outline" class="text-gray-500 text-2xl"></ion-icon>
}
</button>
<span class="text-xs text-gray-400">{{ post.likes }} Likes</span>
</div>
<p class="text-sm text-gray-500 leading-snug italic">{{ post.libelle }}</p>
</div>
</div>
</ion-item>
}
</ion-segment-content>
<ion-segment-content id="friends">
@for (post of postsFriends(); track post.id) {
<ion-item lines="full">
<div class="mt-2">
<div class="flex items-center gap-2 p-3">
<div class="w-8 h-8 rounded-full flex items-center justify-center text-xs bg-teal-100 text-teal-700 max-w-xs">
{{ post.username.substring(0, 2) }}
</div>
<span class="text-lg font-mono ml-2">{{ post.username }}</span>
</div>
<div class="aspect-square overflow-hidden p-1">
<img [src]="post.proof"
alt=""
class="w-full h-full object-cover"/>
</div>
<div class="px-3 py-3">
<div class="flex items-center gap-2 mb-1">
<button class="flex items-center p-0 bg-transparent border-none cursor-pointer"
(click)="likePost(post.id)">
@if (post.isLiked) {
<ion-icon name="heart" class="text-red-700 text-2xl"></ion-icon>
} @else {
<ion-icon name="heart-outline" class="text-gray-500 text-2xl"></ion-icon>
}
</button>
<span class="text-xs text-gray-400">{{ post.likes }} Likes</span>
</div>
<p class="text-sm text-gray-500 leading-snug italic">{{ post.libelle }}</p>
</div>
</div>
</ion-item>
}
</ion-segment-content>
</ion-segment-view>
+82
View File
@@ -0,0 +1,82 @@
import {Component, inject, input, OnInit, signal} from '@angular/core';
import {FriendsService, GetFriendDto, GetPostDto, PostsService} from "../../services/api";
import {firstValueFrom} from "rxjs";
import {IonicModule, LoadingController, ToastController} from "@ionic/angular";
import {heart, heartOutline} from 'ionicons/icons';
import {addIcons} from "ionicons";
addIcons({
"heart": heart,
"heartOutline": heartOutline,
});
@Component({
selector: 'app-post',
templateUrl: './post.component.html',
styleUrls: ['./post.component.scss'],
imports: [
IonicModule
]
})
export class PostComponent implements OnInit {
private postsService = inject(PostsService);
private friendsService = inject(FriendsService);
private loadCtrl = inject(LoadingController);
private toastCtrl = inject(ToastController);
friends = signal<GetFriendDto[]>([]);
posts = input.required<GetPostDto[]>();
localPosts = signal<GetPostDto[]>([]);
postsFriends = signal<GetPostDto[]>([]);
async ngOnInit() {
const loading = await this.loadCtrl.create({
message: 'Chargement...',
spinner: 'lines-sharp-small'
});
await loading.present();
this.localPosts.set(this.posts());
try {
const friends = await firstValueFrom(this.friendsService.getAllFriendsEndpoint());
this.friends.set(friends);
this.postsFriends.set(this.localPosts().filter(post => this.friends().map(x => x.friendId).includes(post.userId)));
} catch {
const toast = await this.toastCtrl.create({
message: 'Erreur lors du chargement des amis.',
duration: 2000,
color: 'danger'
});
await toast.present();
}
await loading.dismiss();
}
async likePost(postId: number) {
this.localPosts.update(x => x.map(x => x.id != postId ? x : {
...x,
likes: x.isLiked ? x.likes - 1 : x.likes + 1,
isLiked: !x.isLiked
}));
try {
await firstValueFrom(this.postsService.patchLikeEndpoint(postId));
this.postsFriends.set(this.localPosts());
} catch {
this.localPosts.update(x => x.map(x => x.id != postId ? x : {
...x,
likes: x.isLiked ? x.likes + 1 : x.likes - 1,
isLiked: !x.isLiked
}));
const toast = await this.toastCtrl.create({
message: 'Impossible d\'aimer ce contenu',
duration: 2000,
color: 'danger'
});
await toast.present();
}
}
}
@@ -90,9 +90,9 @@ export class ProfilFormComponent implements OnInit {
await loading2.dismiss(); await loading2.dismiss();
await toast.present(); await toast.present();
} catch { } catch (e) {
const toast = await this.toastCtrl.create({ const toast = await this.toastCtrl.create({
message: 'Modification impossible', message: e.error ?? 'Modification impossible',
duration: 2000, duration: 2000,
color: 'danger' color: 'danger'
}); });
@@ -1,8 +0,0 @@
<ion-header>
<ion-toolbar>
<ion-title class="font-mono">Mes groupes</ion-title>
</ion-toolbar>
</ion-header>
<ion-content style="--background: #f7f6f2;">
</ion-content>
-13
View File
@@ -1,13 +0,0 @@
import {Component} from '@angular/core';
import {IonicModule} from "@ionic/angular";
@Component({
selector: 'app-groups',
templateUrl: './groups.component.html',
styleUrls: ['./groups.component.scss'],
imports: [
IonicModule
]
})
export class GroupsComponent {
}
+2 -2
View File
@@ -39,7 +39,7 @@
<ion-content style="--background: #f7f6f2;"> <ion-content style="--background: #f7f6f2;">
<div class="mt-4"> <div class="mt-4">
<app-title-part textInfo="Défi bonus du jour"></app-title-part> <app-title-part textInfo="Défi bonus du jour"></app-title-part>
<app-challenge-card [data]="randomChallenge()"></app-challenge-card> <app-challenge-card [challenge]="randomChallenge()"></app-challenge-card>
</div> </div>
<div class="mt-4"> <div class="mt-4">
@@ -49,7 +49,7 @@
@if (groupSelected()) { @if (groupSelected()) {
<app-modal> <app-modal>
test <app-message [groupId]="groupSelected().id"></app-message>
</app-modal> </app-modal>
} }
</ion-content> </ion-content>
+4 -3
View File
@@ -5,8 +5,7 @@ import {personOutline, addOutline, settingsOutline, informationCircleOutline} fr
import {TitlePartComponent} from "../../components/title-part/title-part.component"; import {TitlePartComponent} from "../../components/title-part/title-part.component";
import {ChallengeCardComponent} from "../../components/challenge-card/challenge-card.component"; import {ChallengeCardComponent} from "../../components/challenge-card/challenge-card.component";
import { import {
AchievementsService, CreateGroupDto, AchievementsService, GetAchievementDto, GetGroupDetailsDto, GetGroupDto,
GetAchievementDto, GetGroupDetailsDto, GetGroupDto,
GetRandomChallengeDto, GetRandomChallengeDto,
GetUserChallengeDto, GetUserChallengeDto,
GetUserDetailsDto, GroupsService, GetUserDetailsDto, GroupsService,
@@ -28,6 +27,7 @@ import {GalleryComponent} from "../../components/gallery/gallery.component";
import {GroupsComponent} from "../../components/groups/groups.component"; import {GroupsComponent} from "../../components/groups/groups.component";
import {GroupFormComponent} from "../../components/group-form/group-form.component"; import {GroupFormComponent} from "../../components/group-form/group-form.component";
import {GroupInfoComponent} from "../../components/group-info/group-info.component"; import {GroupInfoComponent} from "../../components/group-info/group-info.component";
import {MessageComponent} from "../../components/message/message.component";
addIcons({ addIcons({
'profile': personOutline, 'profile': personOutline,
@@ -59,6 +59,7 @@ type View = 'menu' | 'profile' | 'password' | 'designation' | 'gallery' | 'group
GroupsComponent, GroupsComponent,
GroupFormComponent, GroupFormComponent,
GroupInfoComponent, GroupInfoComponent,
MessageComponent,
] ]
}) })
export class HomeComponent implements OnInit { export class HomeComponent implements OnInit {
@@ -98,7 +99,7 @@ export class HomeComponent implements OnInit {
this.groups.set(groups); this.groups.set(groups);
} catch { } catch {
const toast = await this.toastCtrl.create({ const toast = await this.toastCtrl.create({
message: 'Impossible de générer un défi aléatoire', message: 'Impossible de générer un défi',
duration: 2000, duration: 2000,
color: 'danger' color: 'danger'
}); });
+2 -1
View File
@@ -101,8 +101,9 @@ export class LoginComponent implements OnInit {
try { try {
const users = this.user().userForm.getRawValue(); const users = this.user().userForm.getRawValue();
await firstValueFrom(this.usersService.createUserEndpoint(users)); await firstValueFrom(this.usersService.createUserEndpoint(users));
await loading.dismiss();
return true; return true;
} catch (e) { } catch {
const toast = await this.toastCtrl.create({ const toast = await this.toastCtrl.create({
message: 'Email ou mot de passe incorrect', message: 'Email ou mot de passe incorrect',
duration: 2000, duration: 2000,
@@ -0,0 +1,28 @@
<ion-header>
<ion-toolbar>
<ion-title class="font-mono">Publications</ion-title>
</ion-toolbar>
</ion-header>
<ion-content style="--background: #f7f6f2;">
@if (posts().length) {
<app-post [posts]="posts()"></app-post>
} @else {
<ion-item lines="none" class="mt-[60%]" style="--background: #F7F6F2;">
<div class="flex flex-col items-center w-full gap-3">
<div class="relative w-14 h-14">
<div class="w-14 h-14 rounded-full bg-white border border-stone-200 flex items-center justify-center">
<ion-icon name="planet" class="text-2xl text-stone-400"></ion-icon>
</div>
<div class="absolute -bottom-0.5 -right-0.5 w-5 h-5 rounded-full bg-white border border-stone-200 flex items-center justify-center">
<ion-icon name="moon-outline" class="text-[10px] text-stone-400"></ion-icon>
</div>
</div>
<div class="text-center flex flex-col gap-1">
<p class="m-0 text-sm font-medium text-stone-400">C'est bien vide par ici</p>
<p class="m-0 text-xs text-stone-300 leading-relaxed">Les challengers dorment encore</p>
</div>
</div>
</ion-item>
}
</ion-content>
@@ -0,0 +1,50 @@
import {Component, inject, signal} from '@angular/core';
import {IonicModule, LoadingController, ToastController} from "@ionic/angular";
import {GetPostDto, PostsService} from "../../services/api";
import {firstValueFrom} from "rxjs";
import {PostComponent} from "../../components/post/post.component";
import {planet, moonOutline} from 'ionicons/icons';
import {addIcons} from "ionicons";
addIcons({
"planet": planet,
"moon-outline": moonOutline,
})
@Component({
selector: 'app-publication',
templateUrl: './publication.component.html',
styleUrls: ['./publication.component.scss'],
imports: [
IonicModule,
PostComponent
]
})
export class PublicationComponent {
private postsService = inject(PostsService);
private loadCtrl = inject(LoadingController);
private toastCtrl = inject(ToastController);
posts = signal<GetPostDto[]>([]);
async ionViewWillEnter() {
const loading = await this.loadCtrl.create({
message: 'Chargement...',
spinner: 'lines-sharp-small'
});
await loading.present();
try {
const posts = await firstValueFrom(this.postsService.getAllPostsEndpoint());
this.posts.set(posts);
} catch {
const toast = await this.toastCtrl.create({
message: 'Impossible de charger les publications.',
duration: 2000,
color: 'danger'
});
await toast.present();
}
await loading.dismiss();
}
}
+1 -5
View File
@@ -11,17 +11,13 @@ import {firstValueFrom} from "rxjs";
IonicModule IonicModule
] ]
}) })
export class RankingComponent implements OnInit { export class RankingComponent {
private overallRankingService = inject(OverallrankingService); private overallRankingService = inject(OverallrankingService);
private loadCtrl = inject(LoadingController); private loadCtrl = inject(LoadingController);
private toastCtrl = inject(ToastController); private toastCtrl = inject(ToastController);
users = signal<GetUserDto[]>([]); users = signal<GetUserDto[]>([]);
async ngOnInit() {
await this.fetchUsers();
}
async ionViewWillEnter() { async ionViewWillEnter() {
await this.fetchUsers(); await this.fetchUsers();
} }
+1 -14
View File
@@ -24,23 +24,10 @@ addIcons({
SearchFriendComponent SearchFriendComponent
] ]
}) })
export class SocialComponent implements OnInit { export class SocialComponent {
private friendsState = inject(FriendsStateService); private friendsState = inject(FriendsStateService);
private loadCtrl = inject(LoadingController); private loadCtrl = inject(LoadingController);
async ngOnInit() {
const loading = await this.loadCtrl.create({
message: 'Chargement...',
spinner: 'lines-sharp-small'
});
await loading.present();
await this.friendsState.fetchFriendsRequest();
await this.friendsState.fetchFriends();
await loading.dismiss();
}
async ionViewWillEnter() { async ionViewWillEnter() {
const loading = await this.loadCtrl.create({ const loading = await this.loadCtrl.create({
message: 'Chargement...', message: 'Chargement...',
+21 -21
View File
@@ -59,9 +59,9 @@ In your Angular project:
```typescript ```typescript
import {ApplicationConfig} from '@angular/core'; import { ApplicationConfig } from '@angular/core';
import {provideHttpClient} from '@angular/common/http'; import { provideHttpClient } from '@angular/common/http';
import {provideApi} from ''; import { provideApi } from '';
export const appConfig: ApplicationConfig = { export const appConfig: ApplicationConfig = {
providers: [ providers: [
@@ -77,15 +77,15 @@ If you're still using `AppModule` and haven't [migrated](https://angular.dev/ref
can still import an Angular module: can still import an Angular module:
```typescript ```typescript
import {ApiModule} from ''; import { ApiModule } from '';
``` ```
If different from the generated base path, during app bootstrap, you can provide the base path to your service. If different from the generated base path, during app bootstrap, you can provide the base path to your service.
```typescript ```typescript
import {ApplicationConfig} from '@angular/core'; import { ApplicationConfig } from '@angular/core';
import {provideHttpClient} from '@angular/common/http'; import { provideHttpClient } from '@angular/common/http';
import {provideApi} from ''; import { provideApi } from '';
export const appConfig: ApplicationConfig = { export const appConfig: ApplicationConfig = {
providers: [ providers: [
@@ -98,9 +98,9 @@ export const appConfig: ApplicationConfig = {
```typescript ```typescript
// with a custom configuration // with a custom configuration
import {ApplicationConfig} from '@angular/core'; import { ApplicationConfig } from '@angular/core';
import {provideHttpClient} from '@angular/common/http'; import { provideHttpClient } from '@angular/common/http';
import {provideApi} from ''; import { provideApi } from '';
export const appConfig: ApplicationConfig = { export const appConfig: ApplicationConfig = {
providers: [ providers: [
@@ -117,9 +117,9 @@ export const appConfig: ApplicationConfig = {
```typescript ```typescript
// with factory building a custom configuration // with factory building a custom configuration
import {ApplicationConfig} from '@angular/core'; import { ApplicationConfig } from '@angular/core';
import {provideHttpClient} from '@angular/common/http'; import { provideHttpClient } from '@angular/common/http';
import {provideApi, Configuration} from ''; import { provideApi, Configuration } from '';
export const appConfig: ApplicationConfig = { export const appConfig: ApplicationConfig = {
providers: [ providers: [
@@ -128,10 +128,10 @@ export const appConfig: ApplicationConfig = {
{ {
provide: Configuration, provide: Configuration,
useFactory: (authService: AuthService) => new Configuration({ useFactory: (authService: AuthService) => new Configuration({
basePath: 'http://localhost:9999', basePath: 'http://localhost:9999',
withCredentials: true, withCredentials: true,
username: authService.getUsername(), username: authService.getUsername(),
password: authService.getPassword(), password: authService.getPassword(),
}), }),
deps: [AuthService], deps: [AuthService],
multi: false multi: false
@@ -147,10 +147,10 @@ you can create an alias name when importing the modules
in order to avoid naming conflicts: in order to avoid naming conflicts:
```typescript ```typescript
import {provideApi as provideUserApi} from 'my-user-api-path'; import { provideApi as provideUserApi } from 'my-user-api-path';
import {provideApi as provideAdminApi} from 'my-admin-api-path'; import { provideApi as provideAdminApi } from 'my-admin-api-path';
import {HttpClientModule} from '@angular/common/http'; import { HttpClientModule } from '@angular/common/http';
import {environment} from '../environments/environment'; import { environment } from '../environments/environment';
export const appConfig: ApplicationConfig = { export const appConfig: ApplicationConfig = {
providers: [ providers: [
@@ -39,98 +39,6 @@ export class MessagesService extends BaseService {
super(basePath, configuration); super(basePath, configuration);
} }
/**
* @endpoint delete /API/Messages/{id}/Groups/{groupId}
* @param id
* @param groupId
* @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 deleteMessageEndpoint(id: number, groupId: number, observe?: 'body', reportProgress?: boolean, options?: {
httpHeaderAccept?: undefined,
context?: HttpContext,
transferCache?: boolean
}): Observable<any>;
public deleteMessageEndpoint(id: number, groupId: number, observe?: 'response', reportProgress?: boolean, options?: {
httpHeaderAccept?: undefined,
context?: HttpContext,
transferCache?: boolean
}): Observable<HttpResponse<any>>;
public deleteMessageEndpoint(id: number, groupId: number, observe?: 'events', reportProgress?: boolean, options?: {
httpHeaderAccept?: undefined,
context?: HttpContext,
transferCache?: boolean
}): Observable<HttpEvent<any>>;
public deleteMessageEndpoint(id: number, groupId: number, observe: any = 'body', reportProgress: boolean = false, options?: {
httpHeaderAccept?: undefined,
context?: HttpContext,
transferCache?: boolean
}): Observable<any> {
if (id === null || id === undefined) {
throw new Error('Required parameter id was null or undefined when calling deleteMessageEndpoint.');
}
if (groupId === null || groupId === undefined) {
throw new Error('Required parameter groupId was null or undefined when calling deleteMessageEndpoint.');
}
let localVarHeaders = this.defaultHeaders;
// authentication (JWTBearerAuth) required
localVarHeaders = this.configuration.addCredentialToHeaders('JWTBearerAuth', 'Authorization', localVarHeaders, 'Bearer ');
const localVarHttpHeaderAcceptSelected: string | undefined = options?.httpHeaderAccept ?? this.configuration.selectHeaderAccept([]);
if (localVarHttpHeaderAcceptSelected !== undefined) {
localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
}
const localVarHttpContext: HttpContext = options?.context ?? new HttpContext();
const localVarTransferCache: boolean = options?.transferCache ?? true;
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/Messages/${this.configuration.encodeParam({
name: "id",
value: id,
in: "path",
style: "simple",
explode: false,
dataType: "number",
dataFormat: "int32"
})}/Groups/${this.configuration.encodeParam({
name: "groupId",
value: groupId,
in: "path",
style: "simple",
explode: false,
dataType: "number",
dataFormat: "int32"
})}`;
const {basePath, withCredentials} = this.configuration;
return this.httpClient.request<any>('delete', `${basePath}${localVarPath}`,
{
context: localVarHttpContext,
responseType: <any>responseType_,
...(withCredentials ? {withCredentials} : {}),
headers: localVarHeaders,
observe: observe,
...(localVarTransferCache !== undefined ? {transferCache: localVarTransferCache} : {}),
reportProgress: reportProgress
}
);
}
/** /**
* @endpoint get /API/Messages/Groups/{groupId} * @endpoint get /API/Messages/Groups/{groupId}
* @param groupId * @param groupId
@@ -190,28 +190,27 @@ export class RandomchallengesService extends BaseService {
/** /**
* @endpoint patch /API/RandomChallenges/{randomChallengeId}/Proof * @endpoint patch /API/RandomChallenges/{randomChallengeId}/Proof
* @param randomChallengeId * @param randomChallengeId
* @param libelle
* @param proof * @param proof
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @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 reportProgress flag to report request and response progress.
* @param options additional options * @param options additional options
*/ */
public patchProofEndpoint(randomChallengeId: number, libelle?: string, proof?: Blob, observe?: 'body', reportProgress?: boolean, options?: { public patchProofEndpoint(randomChallengeId: number, proof?: Blob, observe?: 'body', reportProgress?: boolean, options?: {
httpHeaderAccept?: undefined, httpHeaderAccept?: undefined,
context?: HttpContext, context?: HttpContext,
transferCache?: boolean transferCache?: boolean
}): Observable<any>; }): Observable<any>;
public patchProofEndpoint(randomChallengeId: number, libelle?: string, proof?: Blob, observe?: 'response', reportProgress?: boolean, options?: { public patchProofEndpoint(randomChallengeId: number, proof?: Blob, observe?: 'response', reportProgress?: boolean, options?: {
httpHeaderAccept?: undefined, httpHeaderAccept?: undefined,
context?: HttpContext, context?: HttpContext,
transferCache?: boolean transferCache?: boolean
}): Observable<HttpResponse<any>>; }): Observable<HttpResponse<any>>;
public patchProofEndpoint(randomChallengeId: number, libelle?: string, proof?: Blob, observe?: 'events', reportProgress?: boolean, options?: { public patchProofEndpoint(randomChallengeId: number, proof?: Blob, observe?: 'events', reportProgress?: boolean, options?: {
httpHeaderAccept?: undefined, httpHeaderAccept?: undefined,
context?: HttpContext, context?: HttpContext,
transferCache?: boolean transferCache?: boolean
}): Observable<HttpEvent<any>>; }): Observable<HttpEvent<any>>;
public patchProofEndpoint(randomChallengeId: number, libelle?: string, proof?: Blob, observe: any = 'body', reportProgress: boolean = false, options?: { public patchProofEndpoint(randomChallengeId: number, proof?: Blob, observe: any = 'body', reportProgress: boolean = false, options?: {
httpHeaderAccept?: undefined, httpHeaderAccept?: undefined,
context?: HttpContext, context?: HttpContext,
transferCache?: boolean transferCache?: boolean
@@ -253,9 +252,6 @@ export class RandomchallengesService extends BaseService {
localVarFormParams = new HttpParams({encoder: this.encoder}); localVarFormParams = new HttpParams({encoder: this.encoder});
} }
if (libelle !== undefined) {
localVarFormParams = localVarFormParams.append('libelle', <any>libelle) as any || localVarFormParams;
}
if (proof !== undefined) { if (proof !== undefined) {
localVarFormParams = localVarFormParams.append('proof', <any>proof) as any || localVarFormParams; localVarFormParams = localVarFormParams.append('proof', <any>proof) as any || localVarFormParams;
} }
@@ -11,6 +11,5 @@
export interface CreateMessageDto { export interface CreateMessageDto {
libelle: string; libelle: string;
sendDate: string;
} }
@@ -15,6 +15,7 @@ export interface GetPostDto {
creationDate?: string; creationDate?: string;
likes?: number; likes?: number;
isLiked?: boolean; isLiked?: boolean;
proof?: string | null;
userId?: number; userId?: number;
username?: string | null; username?: string | null;
} }
+46
View File
@@ -0,0 +1,46 @@
import {Injectable, signal} from "@angular/core";
import {GetMessageDto} from "./api";
import * as signalR from '@microsoft/signalr';
@Injectable({
providedIn: 'root'
})
export class ChatService {
private hubConnection!: signalR.HubConnection;
messages = signal<GetMessageDto[]>([]);
async connect(groupId: number) {
if (this.hubConnection) return;
this.hubConnection = new signalR.HubConnectionBuilder()
.withUrl(`http://localhost:5235/groupHub?groupId=${groupId}`)
.withAutomaticReconnect()
.build();
await this.hubConnection.start();
this.hubConnection.on(
'ReceiveMessage',
(message: GetMessageDto) => {
this.messages.update(messages => [
...messages,
message
]);
}
);
}
async disconnect() {
if (this.hubConnection) {
await this.hubConnection.stop();
this.hubConnection = undefined!;
}
}
setMessages(messages: GetMessageDto[]) {
this.messages.set(messages);
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
import {inject, Injectable, signal} from '@angular/core'; import {inject, Injectable, signal} from '@angular/core';
import {FriendsService, GetFriendDto, GetFriendRequestDto} from "./api"; import {FriendsService, GetFriendDto, GetFriendRequestDto} from "./api";
import {LoadingController, ToastController} from "@ionic/angular"; import {ToastController} from "@ionic/angular";
import {firstValueFrom} from "rxjs"; import {firstValueFrom} from "rxjs";
@Injectable({ @Injectable({