Mess commit.
This commit is contained in:
@@ -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<Picture> getAllOfCurrentUser() {
|
||||
User authenticatedUser = userUseCases.getAuthenticatedUser()
|
||||
.orElseThrow(AuthenticationRequiredException::new);
|
||||
|
||||
return picturePort.findAllByPublisherId(authenticatedUser.id());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
) {}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Picture> findAllByPublisherId(UUID id);
|
||||
}
|
||||
|
||||
@@ -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<PictureDto> getAllPicturesOfCurrentUser() {
|
||||
return pictureUseCases.getAllOfCurrentUser()
|
||||
.stream()
|
||||
.map(PictureDto::new)
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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<Picture> findAllByPublisherId(UUID id) {
|
||||
return repository.findAllByPublisherId(id)
|
||||
.stream()
|
||||
.map(PictureEntity::toDomain)
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<PictureEntity, UUID> {
|
||||
List<PictureEntity> findAllByPublisherId(UUID id);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
);
|
||||
|
||||
@@ -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 },
|
||||
]
|
||||
};
|
||||
|
||||
23
frontend/src/app/core/interceptor/jwt.interceptor.ts
Normal file
23
frontend/src/app/core/interceptor/jwt.interceptor.ts
Normal file
@@ -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<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface Picture {
|
||||
id: string,
|
||||
publishedAt: Date
|
||||
}
|
||||
@@ -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<Picture[]> {
|
||||
return lastValueFrom(this.httpClient.get<Picture[]>('/api/pictures/current-user'));
|
||||
}
|
||||
|
||||
uploadPicture(pictureFile: File): Promise<string> {
|
||||
const formData = new FormData();
|
||||
formData.append("file", pictureFile);
|
||||
return lastValueFrom(this.httpClient.post<string>('/api/pictures', formData));
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
<button type="button" class="close" (click)="closeDialog()">
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
<header>
|
||||
<h1>Select an illustration:</h1>
|
||||
</header>
|
||||
<div class="picture-container">
|
||||
@if (isLoading) {
|
||||
<h2>Pictures loading...</h2>
|
||||
<mat-spinner></mat-spinner>
|
||||
} @else {
|
||||
@if (pictures.length) {
|
||||
@for(picture of pictures; track picture) {
|
||||
<img src="/api/pictures/{{picture.id}}" (click)="selectPicture(picture)" matTooltip="Choose this illustration"/>
|
||||
}
|
||||
} @else {
|
||||
<h2>There is no any picture.</h2>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
<footer>
|
||||
<button type="button" class="secondary" matRipple (click)="closeDialog()">
|
||||
Cancel
|
||||
</button>
|
||||
<button type="button" (click)="fileUpload.click()" matRipple>
|
||||
<mat-icon>upload_file</mat-icon>
|
||||
Add new picture
|
||||
</button>
|
||||
<input type="file" (change)="uploadPicture($event)" #fileUpload/>
|
||||
</footer>
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<PictureSelectionDialog>);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -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<PictureSelectionDialog>);
|
||||
|
||||
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 });
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -2,35 +2,36 @@
|
||||
<mat-spinner></mat-spinner>
|
||||
}
|
||||
@else {
|
||||
<!-- <ng-template #afterLoadingPart> -->
|
||||
@if (publication) {
|
||||
<form [formGroup]="publicationEditionForm" (submit)="save()" ngNativeValidate>
|
||||
<header>
|
||||
<h1>Modification de l'article {{ publication.title }}</h1>
|
||||
|
||||
</header>
|
||||
|
||||
<mat-tab-group dynamicHeight>
|
||||
<mat-tab label="Edition">
|
||||
<div class="form-content">
|
||||
<mat-form-field class="example-form-field">
|
||||
<div class="first-part">
|
||||
<div>
|
||||
<mat-form-field>
|
||||
<mat-label>Title</mat-label>
|
||||
<input matInput type="text" formControlName="title" />
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field class="example-form-field">
|
||||
<mat-label>Picture</mat-label>
|
||||
<input matInput type="text" formControlName="illustrationId" />
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field class="example-form-field">
|
||||
<mat-form-field>
|
||||
<mat-label>Description</mat-label>
|
||||
<input matInput type="text" formControlName="description" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<img src="/api/pictures/{{publication.illustrationId}}" (click)="displayPictureSectionDialog()" matTooltip="Click to change illustration"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<mat-form-field class="example-form-field">
|
||||
<mat-label>Content</mat-label>
|
||||
<textarea matInput formControlName="text"></textarea>
|
||||
<textarea matInput formControlName="text" class="text-input"></textarea>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</mat-tab>
|
||||
@@ -55,9 +56,4 @@
|
||||
<h1>Publication failed to load...</h1>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- <ng-template #loadingFailedMessage>
|
||||
|
||||
</ng-template> -->
|
||||
<!-- </ng-template> -->
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<boolean>(false);
|
||||
private publicationSubject = new BehaviorSubject<Publication>(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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user