From 56ac024cbade62db533fa3b145be2bb692974a72 Mon Sep 17 00:00:00 2001 From: Florian THIERRY Date: Mon, 19 Aug 2024 22:42:42 +0200 Subject: [PATCH] Mess commit. --- .../application/picture/PictureUseCases.java | 8 ++ .../codiki/domain/picture/model/Picture.java | 2 + .../picture/model/builder/PictureBuilder.java | 9 +- .../domain/picture/port/PicturePort.java | 3 + .../exposition/picture/PictureController.java | 10 ++ .../exposition/picture/model/PictureDto.java | 15 +++ .../picture/PictureJpaAdapter.java | 9 ++ .../picture/model/PictureEntity.java | 5 +- .../picture/repository/PictureRepository.java | 3 + .../001-initial-script-tables-creation.sql | 1 + frontend/src/app/app.config.ts | 8 +- .../app/core/interceptor/jwt.interceptor.ts | 23 +++++ .../rest-services/picture/model/picture.ts | 4 + .../picture/picture.rest-service.ts | 21 ++++ .../core/service/authentication.service.ts | 8 ++ .../picture-selection-dialog.component.html | 30 ++++++ .../picture-selection-dialog.component.scss | 97 +++++++++++++++++++ .../picture-selection-dialog.component.ts | 55 +++++++++++ .../picture-selection-dialog.service.ts | 24 +++++ .../publication-edition.component.html | 40 ++++---- .../publication-edition.component.scss | 39 +++++++- .../publication-edition.component.ts | 9 +- .../publication-edition.service.ts | 41 ++++++-- 23 files changed, 428 insertions(+), 36 deletions(-) create mode 100644 backend/codiki-exposition/src/main/java/org/codiki/exposition/picture/model/PictureDto.java create mode 100644 frontend/src/app/core/interceptor/jwt.interceptor.ts create mode 100644 frontend/src/app/core/rest-services/picture/model/picture.ts create mode 100644 frontend/src/app/core/rest-services/picture/picture.rest-service.ts create mode 100644 frontend/src/app/pages/publication-edition/picture-selection-dialog/picture-selection-dialog.component.html create mode 100644 frontend/src/app/pages/publication-edition/picture-selection-dialog/picture-selection-dialog.component.scss create mode 100644 frontend/src/app/pages/publication-edition/picture-selection-dialog/picture-selection-dialog.component.ts create mode 100644 frontend/src/app/pages/publication-edition/picture-selection-dialog/picture-selection-dialog.service.ts diff --git a/backend/codiki-application/src/main/java/org/codiki/application/picture/PictureUseCases.java b/backend/codiki-application/src/main/java/org/codiki/application/picture/PictureUseCases.java index 76b6fa2..aaa229b 100644 --- a/backend/codiki-application/src/main/java/org/codiki/application/picture/PictureUseCases.java +++ b/backend/codiki-application/src/main/java/org/codiki/application/picture/PictureUseCases.java @@ -1,6 +1,7 @@ package org.codiki.application.picture; import java.io.File; +import java.util.List; import java.util.Optional; import java.util.UUID; @@ -48,4 +49,11 @@ public class PictureUseCases { public boolean existsById(UUID pictureId) { return picturePort.existsById(pictureId); } + + public List getAllOfCurrentUser() { + User authenticatedUser = userUseCases.getAuthenticatedUser() + .orElseThrow(AuthenticationRequiredException::new); + + return picturePort.findAllByPublisherId(authenticatedUser.id()); + } } diff --git a/backend/codiki-domain/src/main/java/org/codiki/domain/picture/model/Picture.java b/backend/codiki-domain/src/main/java/org/codiki/domain/picture/model/Picture.java index b7d2b4c..5950e6f 100644 --- a/backend/codiki-domain/src/main/java/org/codiki/domain/picture/model/Picture.java +++ b/backend/codiki-domain/src/main/java/org/codiki/domain/picture/model/Picture.java @@ -1,10 +1,12 @@ package org.codiki.domain.picture.model; import java.io.File; +import java.time.ZonedDateTime; import java.util.UUID; public record Picture( UUID id, UUID publisherId, + ZonedDateTime publishedAt, File contentFile ) {} diff --git a/backend/codiki-domain/src/main/java/org/codiki/domain/picture/model/builder/PictureBuilder.java b/backend/codiki-domain/src/main/java/org/codiki/domain/picture/model/builder/PictureBuilder.java index 2d9112d..70d685e 100644 --- a/backend/codiki-domain/src/main/java/org/codiki/domain/picture/model/builder/PictureBuilder.java +++ b/backend/codiki-domain/src/main/java/org/codiki/domain/picture/model/builder/PictureBuilder.java @@ -1,6 +1,7 @@ package org.codiki.domain.picture.model.builder; import java.io.File; +import java.time.ZonedDateTime; import java.util.UUID; import org.codiki.domain.picture.model.Picture; @@ -9,6 +10,7 @@ import org.codiki.domain.user.model.User; public class PictureBuilder { private UUID id; private UUID publisherId; + private ZonedDateTime publishedAt; private File contentFile; private PictureBuilder() {} @@ -37,12 +39,17 @@ public class PictureBuilder { return withPublisherId(publisher.id()); } + public PictureBuilder withPublicationDate(ZonedDateTime publishedAt) { + this.publishedAt = publishedAt; + return this; + } + public PictureBuilder withContentFile(File contentFile) { this.contentFile = contentFile; return this; } public Picture build() { - return new Picture(id, publisherId, contentFile); + return new Picture(id, publisherId, publishedAt, contentFile); } } diff --git a/backend/codiki-domain/src/main/java/org/codiki/domain/picture/port/PicturePort.java b/backend/codiki-domain/src/main/java/org/codiki/domain/picture/port/PicturePort.java index 535f431..8046f9f 100644 --- a/backend/codiki-domain/src/main/java/org/codiki/domain/picture/port/PicturePort.java +++ b/backend/codiki-domain/src/main/java/org/codiki/domain/picture/port/PicturePort.java @@ -1,5 +1,6 @@ package org.codiki.domain.picture.port; +import java.util.List; import java.util.Optional; import java.util.UUID; @@ -13,4 +14,6 @@ public interface PicturePort { void save(Picture picture); void deleteById(UUID pictureId); + + List findAllByPublisherId(UUID id); } diff --git a/backend/codiki-exposition/src/main/java/org/codiki/exposition/picture/PictureController.java b/backend/codiki-exposition/src/main/java/org/codiki/exposition/picture/PictureController.java index 1117c9a..5b053b7 100644 --- a/backend/codiki-exposition/src/main/java/org/codiki/exposition/picture/PictureController.java +++ b/backend/codiki-exposition/src/main/java/org/codiki/exposition/picture/PictureController.java @@ -1,6 +1,7 @@ package org.codiki.exposition.picture; import java.io.File; +import java.util.List; import java.util.UUID; import static org.springframework.http.MediaType.APPLICATION_OCTET_STREAM_VALUE; @@ -8,6 +9,7 @@ import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE; import org.codiki.application.picture.PictureUseCases; import org.codiki.domain.picture.exception.PictureNotFoundException; import org.codiki.domain.picture.model.Picture; +import org.codiki.exposition.picture.model.PictureDto; import org.springframework.core.io.FileSystemResource; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -44,4 +46,12 @@ public class PictureController { .orElseThrow(() -> new PictureNotFoundException(pictureId)); return new FileSystemResource(picture.contentFile()); } + + @GetMapping("/current-user") + public List getAllPicturesOfCurrentUser() { + return pictureUseCases.getAllOfCurrentUser() + .stream() + .map(PictureDto::new) + .toList(); + } } diff --git a/backend/codiki-exposition/src/main/java/org/codiki/exposition/picture/model/PictureDto.java b/backend/codiki-exposition/src/main/java/org/codiki/exposition/picture/model/PictureDto.java new file mode 100644 index 0000000..4546840 --- /dev/null +++ b/backend/codiki-exposition/src/main/java/org/codiki/exposition/picture/model/PictureDto.java @@ -0,0 +1,15 @@ +package org.codiki.exposition.picture.model; + +import org.codiki.domain.picture.model.Picture; + +import java.time.ZonedDateTime; +import java.util.UUID; + +public record PictureDto( + UUID id, + ZonedDateTime publishedAt +) { + public PictureDto(Picture picture) { + this(picture.id(), picture.publishedAt()); + } +} diff --git a/backend/codiki-infrastructure/src/main/java/org/codiki/infrastructure/picture/PictureJpaAdapter.java b/backend/codiki-infrastructure/src/main/java/org/codiki/infrastructure/picture/PictureJpaAdapter.java index 213e391..b8b2d13 100644 --- a/backend/codiki-infrastructure/src/main/java/org/codiki/infrastructure/picture/PictureJpaAdapter.java +++ b/backend/codiki-infrastructure/src/main/java/org/codiki/infrastructure/picture/PictureJpaAdapter.java @@ -1,6 +1,7 @@ package org.codiki.infrastructure.picture; import java.io.File; +import java.util.List; import java.util.Optional; import java.util.UUID; @@ -66,4 +67,12 @@ public class PictureJpaAdapter implements PicturePort { public void deleteById(UUID pictureId) { repository.deleteById(pictureId); } + + @Override + public List findAllByPublisherId(UUID id) { + return repository.findAllByPublisherId(id) + .stream() + .map(PictureEntity::toDomain) + .toList(); + } } diff --git a/backend/codiki-infrastructure/src/main/java/org/codiki/infrastructure/picture/model/PictureEntity.java b/backend/codiki-infrastructure/src/main/java/org/codiki/infrastructure/picture/model/PictureEntity.java index 6c56ff7..f81c1f2 100644 --- a/backend/codiki-infrastructure/src/main/java/org/codiki/infrastructure/picture/model/PictureEntity.java +++ b/backend/codiki-infrastructure/src/main/java/org/codiki/infrastructure/picture/model/PictureEntity.java @@ -1,5 +1,6 @@ package org.codiki.infrastructure.picture.model; +import java.time.ZonedDateTime; import java.util.UUID; import org.codiki.domain.picture.model.Picture; @@ -24,6 +25,8 @@ public class PictureEntity { private UUID id; @Column(nullable = false) private UUID publisherId; + @Column(nullable = false) + private ZonedDateTime publishedAt; public PictureEntity(Picture picture) { id = picture.id(); @@ -31,6 +34,6 @@ public class PictureEntity { } public Picture toDomain() { - return new Picture(id, publisherId, null); + return new Picture(id, publisherId, publishedAt, null); } } diff --git a/backend/codiki-infrastructure/src/main/java/org/codiki/infrastructure/picture/repository/PictureRepository.java b/backend/codiki-infrastructure/src/main/java/org/codiki/infrastructure/picture/repository/PictureRepository.java index 89a035e..d112598 100644 --- a/backend/codiki-infrastructure/src/main/java/org/codiki/infrastructure/picture/repository/PictureRepository.java +++ b/backend/codiki-infrastructure/src/main/java/org/codiki/infrastructure/picture/repository/PictureRepository.java @@ -1,9 +1,12 @@ package org.codiki.infrastructure.picture.repository; +import java.util.List; import java.util.UUID; +import org.codiki.domain.picture.model.Picture; import org.codiki.infrastructure.picture.model.PictureEntity; import org.springframework.data.jpa.repository.JpaRepository; public interface PictureRepository extends JpaRepository { + List findAllByPublisherId(UUID id); } diff --git a/backend/codiki-infrastructure/src/main/resources/sql/001-initial-script-tables-creation.sql b/backend/codiki-infrastructure/src/main/resources/sql/001-initial-script-tables-creation.sql index 2e742fa..077b6bb 100644 --- a/backend/codiki-infrastructure/src/main/resources/sql/001-initial-script-tables-creation.sql +++ b/backend/codiki-infrastructure/src/main/resources/sql/001-initial-script-tables-creation.sql @@ -36,6 +36,7 @@ CREATE INDEX category_parent_category_id_idx ON category (parent_category_id); CREATE TABLE IF NOT EXISTS picture ( id UUID NOT NULL, publisher_id UUID NOT NULL, + published_at TIMESTAMP WITH TIME ZONE NOT NULL, CONSTRAINT picture_pk PRIMARY KEY (id), CONSTRAINT picture_publisher_id_fk FOREIGN KEY (publisher_id) REFERENCES "user" (id) ); diff --git a/frontend/src/app/app.config.ts b/frontend/src/app/app.config.ts index d78e3f0..ab227fa 100644 --- a/frontend/src/app/app.config.ts +++ b/frontend/src/app/app.config.ts @@ -1,14 +1,16 @@ import { ApplicationConfig } from '@angular/core'; import { provideRouter } from '@angular/router'; -import { routes } from './app.routes'; +import { HTTP_INTERCEPTORS, provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'; import { provideAnimationsAsync } from '@angular/platform-browser/animations/async'; -import { provideHttpClient } from '@angular/common/http'; +import { routes } from './app.routes'; +import { JwtInterceptor } from './core/interceptor/jwt.interceptor'; export const appConfig: ApplicationConfig = { providers: [ provideRouter(routes), provideAnimationsAsync(), - provideHttpClient() + provideHttpClient(withInterceptorsFromDi()), + { provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true }, ] }; diff --git a/frontend/src/app/core/interceptor/jwt.interceptor.ts b/frontend/src/app/core/interceptor/jwt.interceptor.ts new file mode 100644 index 0000000..84e9da1 --- /dev/null +++ b/frontend/src/app/core/interceptor/jwt.interceptor.ts @@ -0,0 +1,23 @@ +import { Observable } from 'rxjs'; +import { inject, Injectable } from '@angular/core'; +import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http'; +import { AuthenticationService } from '../service/authentication.service'; + +@Injectable() +export class JwtInterceptor implements HttpInterceptor { + private readonly authenticationService = inject(AuthenticationService); + + intercept(request: HttpRequest, next: HttpHandler): Observable> { + const jwt = this.authenticationService.getToken(); + + if (jwt) { + const cloned = request.clone({ + headers: request.headers.set('Authorization', `Bearer ${jwt}`) + }); + + return next.handle(cloned); + } + + return next.handle(request); + } +} \ No newline at end of file diff --git a/frontend/src/app/core/rest-services/picture/model/picture.ts b/frontend/src/app/core/rest-services/picture/model/picture.ts new file mode 100644 index 0000000..24d35ba --- /dev/null +++ b/frontend/src/app/core/rest-services/picture/model/picture.ts @@ -0,0 +1,4 @@ +export interface Picture { + id: string, + publishedAt: Date +} \ No newline at end of file diff --git a/frontend/src/app/core/rest-services/picture/picture.rest-service.ts b/frontend/src/app/core/rest-services/picture/picture.rest-service.ts new file mode 100644 index 0000000..c312077 --- /dev/null +++ b/frontend/src/app/core/rest-services/picture/picture.rest-service.ts @@ -0,0 +1,21 @@ +import { HttpClient, HttpParams } from "@angular/common/http"; +import { inject, Injectable } from "@angular/core"; +import { Picture } from "./model/picture"; +import { lastValueFrom } from "rxjs"; + +@Injectable({ + providedIn: 'root' +}) +export class PictureRestService { + private readonly httpClient = inject(HttpClient); + + getAllOfCurrentUser(): Promise { + return lastValueFrom(this.httpClient.get('/api/pictures/current-user')); + } + + uploadPicture(pictureFile: File): Promise { + const formData = new FormData(); + formData.append("file", pictureFile); + return lastValueFrom(this.httpClient.post('/api/pictures', formData)); + } +} \ No newline at end of file diff --git a/frontend/src/app/core/service/authentication.service.ts b/frontend/src/app/core/service/authentication.service.ts index 117f4d6..9361672 100644 --- a/frontend/src/app/core/service/authentication.service.ts +++ b/frontend/src/app/core/service/authentication.service.ts @@ -36,6 +36,14 @@ export class AuthenticationService { return result; } + getAuthenticatedUser(): User | undefined { + return this.extractUserFromLocalStorage(); + } + + getToken(): string | undefined { + return localStorage.getItem(JWT_PARAM) ?? undefined; + } + private extractUserFromLocalStorage(): User | undefined { let result: User | undefined = undefined; diff --git a/frontend/src/app/pages/publication-edition/picture-selection-dialog/picture-selection-dialog.component.html b/frontend/src/app/pages/publication-edition/picture-selection-dialog/picture-selection-dialog.component.html new file mode 100644 index 0000000..431e4f7 --- /dev/null +++ b/frontend/src/app/pages/publication-edition/picture-selection-dialog/picture-selection-dialog.component.html @@ -0,0 +1,30 @@ + +
+

Select an illustration:

+
+
+ @if (isLoading) { +

Pictures loading...

+ + } @else { + @if (pictures.length) { + @for(picture of pictures; track picture) { + + } + } @else { +

There is no any picture.

+ } + } +
+
+ + + +
\ No newline at end of file diff --git a/frontend/src/app/pages/publication-edition/picture-selection-dialog/picture-selection-dialog.component.scss b/frontend/src/app/pages/publication-edition/picture-selection-dialog/picture-selection-dialog.component.scss new file mode 100644 index 0000000..07a3d23 --- /dev/null +++ b/frontend/src/app/pages/publication-edition/picture-selection-dialog/picture-selection-dialog.component.scss @@ -0,0 +1,97 @@ +:host { + display: flex; + flex-direction: column; + padding: 1em; + gap: 1em; + position: relative; + max-height: 90vh; + + .close { + position: absolute; + top: 1em; + right: 1em; + width: 2.5em; + height: 2.5em; + border-radius: 10em; + border: none; + display: flex; + justify-content: center; + align-items: center; + cursor: pointer; + } + + header { + flex: 1; + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + } + + .picture-container { + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: center; + align-items: center; + gap: 1em; + max-height: 30em; + overflow-y: auto; + min-height: 10em; + padding: .5em 0; + + img { + width: 15em; + height: 10em; + object-fit: cover; + border-radius: 1em; + opacity: .9; + transition: opacity .2s ease-in-out, box-shadow .2s ease-in-out; + box-shadow: 0 2px 5px 0 rgba(0,0,0,.16),0 2px 10px 0 rgba(0,0,0,.12); + + &:hover { + opacity: 1; + cursor: pointer; + box-shadow: 0 2px 5px 0 rgba(0,0,0,.32),0 2px 10px 0 rgba(0,0,0,.24); + } + } + } + + footer { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + + button { + padding: .8em 1.2em; + border-radius: 10em; + border: none; + background-color: #3f51b5; + color: white; + transition: background-color .2s ease-in-out; + cursor: pointer; + display: flex; + justify-content: center; + align-items: center; + + &:hover { + background-color: #5b6ed8; + } + + &.secondary { + color: #3f51b5; + background-color: white; + + &:hover { + background-color: #f2f4ff; + cursor: pointer; + } + } + } + + input[type=file] { + display: none; + } + } +} \ No newline at end of file diff --git a/frontend/src/app/pages/publication-edition/picture-selection-dialog/picture-selection-dialog.component.ts b/frontend/src/app/pages/publication-edition/picture-selection-dialog/picture-selection-dialog.component.ts new file mode 100644 index 0000000..ac9779e --- /dev/null +++ b/frontend/src/app/pages/publication-edition/picture-selection-dialog/picture-selection-dialog.component.ts @@ -0,0 +1,55 @@ +import { Component, inject, OnInit } from "@angular/core"; +import { Picture } from "../../../core/rest-services/picture/model/picture"; +import {MatProgressSpinnerModule} from '@angular/material/progress-spinner'; +import { MatSnackBar } from "@angular/material/snack-bar"; +import { PictureRestService } from "../../../core/rest-services/picture/picture.rest-service"; +import { MatIcon } from "@angular/material/icon"; +import { MatDialogRef } from "@angular/material/dialog"; +import {MatRippleModule} from '@angular/material/core'; +import { MatTooltip } from "@angular/material/tooltip"; + +@Component({ + selector: 'app-picture-selection', + standalone: true, + imports: [MatProgressSpinnerModule, MatIcon, MatRippleModule, MatTooltip], + templateUrl: './picture-selection-dialog.component.html', + styleUrl: './picture-selection-dialog.component.scss', +}) +export class PictureSelectionDialog implements OnInit { + private readonly pictureRestService = inject(PictureRestService); + private readonly snackBar = inject(MatSnackBar); + private readonly dialogRef = inject(MatDialogRef); + + isLoading: boolean = false; + isLoaded: boolean = false; + pictures: Picture[] = []; + + ngOnInit(): void { + this.isLoading = true; + this.pictureRestService.getAllOfCurrentUser() + .then(pictures => { + this.pictures = pictures; + }) + .catch(error => { + const errorMessage = 'An error occured while loading pictures.'; + console.error(errorMessage, error); + this.snackBar.open(errorMessage, 'Close', { duration: 5000 }); + }) + .finally(() => { + this.isLoading = false; + this.isLoaded = true; + }) + } + + selectPicture(picture: Picture): void { + console.log(picture.id); + } + + closeDialog(): void { + this.dialogRef.close(); + } + + uploadPicture(file: any): void { + console.log("uploadFile", file); + } +} \ No newline at end of file diff --git a/frontend/src/app/pages/publication-edition/picture-selection-dialog/picture-selection-dialog.service.ts b/frontend/src/app/pages/publication-edition/picture-selection-dialog/picture-selection-dialog.service.ts new file mode 100644 index 0000000..6ce42a3 --- /dev/null +++ b/frontend/src/app/pages/publication-edition/picture-selection-dialog/picture-selection-dialog.service.ts @@ -0,0 +1,24 @@ +import { inject, Injectable } from "@angular/core"; +import { PictureRestService } from "../../../core/rest-services/picture/picture.rest-service"; +import { MatSnackBar } from "@angular/material/snack-bar"; +import { MatDialogRef } from "@angular/material/dialog"; +import { PictureSelectionDialog } from "./picture-selection-dialog.component"; + +@Injectable() +export class PictureSelectionDialogService { + private pictureRestService = inject(PictureRestService); + private snackBar = inject(MatSnackBar); + private readonly dialogRef = inject(MatDialogRef); + + uploadPicture(pictureFile: File): void { + this.pictureRestService.uploadPicture(pictureFile) + .then(pictureId => { + this.dialogRef.close(pictureId); + }) + .catch(error => { + const errorMessage = 'An error occured while uploading a picture...'; + console.error(errorMessage, error); + this.snackBar.open(errorMessage, 'Close', { duration: 5000 }); + }); + } +} \ No newline at end of file diff --git a/frontend/src/app/pages/publication-edition/publication-edition.component.html b/frontend/src/app/pages/publication-edition/publication-edition.component.html index 8e0ca8b..422afa6 100644 --- a/frontend/src/app/pages/publication-edition/publication-edition.component.html +++ b/frontend/src/app/pages/publication-edition/publication-edition.component.html @@ -1,36 +1,37 @@ @if ((isLoading$ | async) == true) { -} +} @else { - @if (publication) {

Modification de l'article {{ publication.title }}

-
- - Title - - +
+
+ + Title + + + + Description + + +
+ +
+ +
+
- - Picture - - - - - Description - - Content - +
@@ -55,9 +56,4 @@

Publication failed to load...

} - - - } \ No newline at end of file diff --git a/frontend/src/app/pages/publication-edition/publication-edition.component.scss b/frontend/src/app/pages/publication-edition/publication-edition.component.scss index 52fe7d2..98ef782 100644 --- a/frontend/src/app/pages/publication-edition/publication-edition.component.scss +++ b/frontend/src/app/pages/publication-edition/publication-edition.component.scss @@ -59,7 +59,44 @@ .form-content { padding: 2em; + padding-bottom: 0; display: flex; flex-direction: column; - gap: 1em; + gap: .5em; + + mat-form-field { + textarea { + height: 20em; + } + } + + .first-part { + display: flex; + flex-direction: column-reverse; + gap: 1em; + + @media screen and (min-width: 600px) { + flex-direction: row; + + div { + max-width: 50%; + } + } + + div { + flex: 1 0 50%; + display: flex; + flex-direction: column; + justify-content: center; + + &:nth-last-child { + img { + flex: 1; + object-fit: cover; + width: 100%; + cursor: pointer; + } + } + } + } } \ No newline at end of file diff --git a/frontend/src/app/pages/publication-edition/publication-edition.component.ts b/frontend/src/app/pages/publication-edition/publication-edition.component.ts index e700579..d61b722 100644 --- a/frontend/src/app/pages/publication-edition/publication-edition.component.ts +++ b/frontend/src/app/pages/publication-edition/publication-edition.component.ts @@ -7,11 +7,14 @@ import { MatTabsModule } from '@angular/material/tabs'; import { debounceTime, map, Observable, Subscription } from 'rxjs'; import { Publication } from '../../core/rest-services/publications/model/publication'; import { PublicationEditionService } from './publication-edition.service'; +import {MatDialogModule} from '@angular/material/dialog'; +import { PictureSelectionDialog } from './picture-selection-dialog/picture-selection-dialog.component'; +import { MatTooltipModule } from '@angular/material/tooltip'; @Component({ selector: 'app-publication-edition', standalone: true, - imports: [ReactiveFormsModule, MatInputModule, MatProgressSpinner, MatTabsModule, CommonModule], + imports: [ReactiveFormsModule, MatInputModule, MatProgressSpinner, MatTabsModule, MatDialogModule, CommonModule, PictureSelectionDialog, MatTooltipModule], templateUrl: './publication-edition.component.html', styleUrl: './publication-edition.component.scss', providers: [PublicationEditionService] @@ -85,4 +88,8 @@ export class PublicationEditionComponent implements OnInit, OnDestroy { save(): void { } + + displayPictureSectionDialog(): void { + this.publicationEditionService.displayPictureSectionDialog(); + } } diff --git a/frontend/src/app/pages/publication-edition/publication-edition.service.ts b/frontend/src/app/pages/publication-edition/publication-edition.service.ts index fa24f40..5fbf843 100644 --- a/frontend/src/app/pages/publication-edition/publication-edition.service.ts +++ b/frontend/src/app/pages/publication-edition/publication-edition.service.ts @@ -1,11 +1,14 @@ import { Location } from "@angular/common"; -import { inject, Injectable } from "@angular/core"; +import { inject, Injectable, OnDestroy } from "@angular/core"; import { MatSnackBar } from "@angular/material/snack-bar"; import { ActivatedRoute } from "@angular/router"; -import { BehaviorSubject, Observable } from "rxjs"; +import { BehaviorSubject, Observable, Subscription } from "rxjs"; import { Publication } from "../../core/rest-services/publications/model/publication"; import { PublicationRestService } from "../../core/rest-services/publications/publication.rest-service"; import { copy } from '../../core/utils/ObjectUtils'; +import { MatDialog } from "@angular/material/dialog"; +import { PictureSelectionDialog } from "./picture-selection-dialog/picture-selection-dialog.component"; + const DEFAULT_PUBLICATION: Publication = { id: '', @@ -25,14 +28,20 @@ const DEFAULT_PUBLICATION: Publication = { }; @Injectable() -export class PublicationEditionService { - private activatedRoute = inject(ActivatedRoute); - private publicationRestService = inject(PublicationRestService); - private location = inject(Location); - private snackBar = inject(MatSnackBar); +export class PublicationEditionService implements OnDestroy { + private readonly activatedRoute = inject(ActivatedRoute); + private readonly publicationRestService = inject(PublicationRestService); + private readonly location = inject(Location); + private readonly snackBar = inject(MatSnackBar); + private readonly dialog = inject(MatDialog); private isLoadingSubject = new BehaviorSubject(false); private publicationSubject = new BehaviorSubject(copy(DEFAULT_PUBLICATION)); + private subscriptions: Subscription[] = []; + + ngOnDestroy(): void { + this.subscriptions.forEach(subscription => subscription.unsubscribe()); + } private get _publication(): Publication { return this.publicationSubject.value; @@ -90,4 +99,22 @@ export class PublicationEditionService { publication.text = text; this._save(publication); } + + editIllustrationId(pictureId: string): void { + const publication = this._publication; + publication.illustrationId = pictureId + this._save(publication); + } + + displayPictureSectionDialog(): void { + const dialogRef = this.dialog.open(PictureSelectionDialog); + + const afterDialogCloseSubscription = dialogRef.afterClosed() + .subscribe(newPictureId => { + if (newPictureId) { + this.editIllustrationId(newPictureId); + } + }); + this.subscriptions.push(afterDialogCloseSubscription); + } } \ No newline at end of file