Implementaiton of preview tab.

This commit is contained in:
Florian THIERRY
2024-09-04 10:26:47 +02:00
parent db669114b2
commit b0cc42fddd
9 changed files with 103 additions and 3 deletions

View File

@@ -195,4 +195,8 @@ public class PublicationUseCases {
public List<Publication> getLatest() { public List<Publication> getLatest() {
return publicationPort.getLatest(); return publicationPort.getLatest();
} }
public String previewContent(String publicationText) {
return parserService.parse(publicationText);
}
} }

View File

@@ -11,6 +11,7 @@ import org.codiki.domain.publication.exception.NoPublicationSearchResultExceptio
import org.codiki.domain.publication.exception.PublicationNotFoundException; import org.codiki.domain.publication.exception.PublicationNotFoundException;
import org.codiki.domain.publication.model.Publication; import org.codiki.domain.publication.model.Publication;
import org.codiki.domain.publication.model.PublicationEditionRequest; import org.codiki.domain.publication.model.PublicationEditionRequest;
import org.codiki.exposition.publication.model.PreviewContentRequest;
import org.codiki.exposition.publication.model.PublicationDto; import org.codiki.exposition.publication.model.PublicationDto;
import org.codiki.exposition.publication.model.PublicationEditionRequestDto; import org.codiki.exposition.publication.model.PublicationEditionRequestDto;
import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.DeleteMapping;
@@ -91,4 +92,9 @@ public class PublicationController {
return publications; return publications;
} }
@PostMapping("/preview")
public String previewPublicationContent(@RequestBody PreviewContentRequest request) {
return publicationUseCases.previewContent(request.text());
}
} }

View File

@@ -0,0 +1,5 @@
package org.codiki.exposition.publication.model;
public record PreviewContentRequest(
String text
) {}

View File

@@ -26,4 +26,9 @@ export class PublicationRestService {
params = params.set('query', searchCriteria); params = params.set('query', searchCriteria);
return lastValueFrom(this.httpClient.get<Publication[]>('/api/publications', { params })); return lastValueFrom(this.httpClient.get<Publication[]>('/api/publications', { params }));
} }
preview(publicationText: string): Promise<string> {
const request = { text: publicationText };
return lastValueFrom(this.httpClient.post<string>('/api/publications/preview', request));
}
} }

View File

@@ -8,7 +8,7 @@
<h1>Modification de l'article {{ publication.title }}</h1> <h1>Modification de l'article {{ publication.title }}</h1>
</header> </header>
<mat-tab-group dynamicHeight> <mat-tab-group dynamicHeight (selectedIndexChange)="onTabChange($event)">
<mat-tab label="Edition"> <mat-tab label="Edition">
<div class="form-content"> <div class="form-content">
<div class="first-part"> <div class="first-part">
@@ -66,7 +66,19 @@
</mat-tab> </mat-tab>
<mat-tab label="Previewing"> <mat-tab label="Previewing">
<div class="preview">
@if ((isPreviewing$ | async) === true) {
<h2>Preview is loading...</h2>
<mat-spinner></mat-spinner>
} @else {
<img class="illustration" src="/pictures/{{ publication.illustrationId }}" />
<header>
<h1>{{ publication.title }}</h1>
<h2>{{ publication.description }}</h2>
</header>
<main [innerHTML]="publication.parsedText"></main>
}
</div>
</mat-tab> </mat-tab>
</mat-tab-group> </mat-tab-group>
<footer> <footer>

View File

@@ -11,7 +11,7 @@
border-radius: .5em; border-radius: .5em;
box-shadow: 0 2px 5px 0 rgba(0,0,0,.16),0 2px 10px 0 rgba(0,0,0,.12); box-shadow: 0 2px 5px 0 rgba(0,0,0,.16),0 2px 10px 0 rgba(0,0,0,.12);
header { & > header {
padding: 2em; padding: 2em;
background-color: #3f51b5; background-color: #3f51b5;
color: white; color: white;
@@ -145,3 +145,38 @@ button, a.button {
} }
} }
} }
.preview {
display: flex;
flex-direction: column;
max-height: 80vh;
overflow-y: auto;
align-items: center;
.illustration {
height: 12em;
object-fit: cover;
transition: height .2s ease-in-out;
@media screen and (min-width: 450px) {
height: 15em;
}
@media screen and (min-width: 600px) {
height: 20em;
}
@media screen and (min-width: 750px) {
height: 25em;
}
}
header {
padding: 2em;
}
main {
padding: 2em;
text-align: justify;
}
}

View File

@@ -55,6 +55,10 @@ export class PublicationEditionComponent implements OnInit, OnDestroy {
return this.publicationEditionService.isSaving$; return this.publicationEditionService.isSaving$;
} }
get isPreviewing$(): Observable<boolean> {
return this.publicationEditionService.isPreviewing$;
}
ngOnInit(): void { ngOnInit(): void {
['title', 'description', 'text'].forEach(fieldName => { ['title', 'description', 'text'].forEach(fieldName => {
const fieldSubscription = this.publicationEditionForm.controls[fieldName].valueChanges const fieldSubscription = this.publicationEditionForm.controls[fieldName].valueChanges
@@ -135,6 +139,11 @@ export class PublicationEditionComponent implements OnInit, OnDestroy {
console.log(`cursor position updated: [${positionStart}, ${positionEnd}] (${selectedCharacterCount})`); console.log(`cursor position updated: [${positionStart}, ${positionEnd}] (${selectedCharacterCount})`);
this.publicationEditionService.editCursorPosition(positionStart, positionEnd); this.publicationEditionService.editCursorPosition(positionStart, positionEnd);
} }
}
onTabChange(tabSelectedIndex: number): void {
if (tabSelectedIndex === 1) {
this.publicationEditionService.loadPreview();
}
} }
} }

View File

@@ -64,6 +64,7 @@ export class PublicationEditionService implements OnDestroy {
private stateSubject = new BehaviorSubject<PublicationEditionState>(copy(DEFAULT_STATE)); private stateSubject = new BehaviorSubject<PublicationEditionState>(copy(DEFAULT_STATE));
private subscriptions: Subscription[] = []; private subscriptions: Subscription[] = [];
private isSavingSubject = new BehaviorSubject<boolean>(false); private isSavingSubject = new BehaviorSubject<boolean>(false);
private isPreviewingSubject = new BehaviorSubject<boolean>(false);
ngOnDestroy(): void { ngOnDestroy(): void {
this.subscriptions.forEach(subscription => subscription.unsubscribe()); this.subscriptions.forEach(subscription => subscription.unsubscribe());
@@ -85,6 +86,10 @@ export class PublicationEditionService implements OnDestroy {
return this.isSavingSubject.asObservable(); return this.isSavingSubject.asObservable();
} }
get isPreviewing$(): Observable<boolean> {
return this.isPreviewingSubject.asObservable();
}
get state$(): Observable<PublicationEditionState> { get state$(): Observable<PublicationEditionState> {
return this.stateSubject.asObservable(); return this.stateSubject.asObservable();
} }
@@ -262,4 +267,19 @@ export class PublicationEditionService implements OnDestroy {
}) })
.finally(() => this.isSavingSubject.next(false)); .finally(() => this.isSavingSubject.next(false));
} }
loadPreview(): void {
const state = this._state;
this.isPreviewingSubject.next(true);
this.publicationRestService.preview(state.publication.text)
.then(parsedText => {
state.publication.parsedText = parsedText;
this._save(state);
})
.catch(error => {
console.error(error);
})
.finally(() => this.isPreviewingSubject.next(false));
}
} }

View File

@@ -8,3 +8,7 @@ body {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
img {
max-width: 100%;
}