From b84ba15f4cd6cd5b64139fcc364f1f40112f4ad7 Mon Sep 17 00:00:00 2001 From: Florian THIERRY Date: Fri, 30 Aug 2024 18:01:29 +0200 Subject: [PATCH] Implementaiton of h1, h2, h3 and link button on publication editor. --- .../publication-edition.component.html | 17 ++- .../publication-edition.component.ts | 38 ++++-- .../publication-edition.service.ts | 113 ++++++++++++++---- 3 files changed, 132 insertions(+), 36 deletions(-) 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 bede172..f0ed70e 100644 --- a/frontend/src/app/pages/publication-edition/publication-edition.component.html +++ b/frontend/src/app/pages/publication-edition/publication-edition.component.html @@ -29,19 +29,19 @@
- - - -
Content - + 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 21e986a..9e89618 100644 --- a/frontend/src/app/pages/publication-edition/publication-edition.component.ts +++ b/frontend/src/app/pages/publication-edition/publication-edition.component.ts @@ -59,7 +59,6 @@ export class PublicationEditionComponent implements OnInit, OnDestroy { ['title', 'description', 'text'].forEach(fieldName => { const fieldSubscription = this.publicationEditionForm.controls[fieldName].valueChanges .pipe( - debounceTime(300), map(value => value?.length ? value as string : '') ) .subscribe(fieldValue => { @@ -76,18 +75,17 @@ export class PublicationEditionComponent implements OnInit, OnDestroy { default: break; } - this.publicationEditionService }); this.subscriptions.push(fieldSubscription); }); - const publicationSubscription = this.publicationEditionService.publication$.subscribe(publication => { - this.publication = publication; - this.publicationEditionForm.controls['title'].setValue(publication.title, { emitEvent: false }); - this.publicationEditionForm.controls['description'].setValue(publication.description, { emitEvent: false }); - this.publicationEditionForm.controls['text'].setValue(publication.text, { emitEvent: false }); - this.publicationEditionForm.controls['illustrationId'].setValue(publication.illustrationId, { emitEvent: false }); - this.publicationEditionForm.controls['categoryId'].setValue(publication.categoryId, { emitEvent: false }); + const publicationSubscription = this.publicationEditionService.state$.subscribe(state => { + this.publication = state.publication; + this.publicationEditionForm.controls['title'].setValue(this.publication.title, { emitEvent: false }); + this.publicationEditionForm.controls['description'].setValue(this.publication.description, { emitEvent: false }); + this.publicationEditionForm.controls['text'].setValue(this.publication.text, { emitEvent: false }); + this.publicationEditionForm.controls['illustrationId'].setValue(this.publication.illustrationId, { emitEvent: false }); + this.publicationEditionForm.controls['categoryId'].setValue(this.publication.categoryId, { emitEvent: false }); }); this.subscriptions.push(publicationSubscription); @@ -102,6 +100,14 @@ export class PublicationEditionComponent implements OnInit, OnDestroy { this.location.back(); } + insertTitle(titleNumber: number): void { + this.publicationEditionService.insertTitle(titleNumber); + } + + insertLink(): void { + this.publicationEditionService.insertLink(); + } + save(): void { this.publicationEditionService.save(); } @@ -109,4 +115,18 @@ export class PublicationEditionComponent implements OnInit, OnDestroy { displayPictureSectionDialog(): void { this.publicationEditionService.displayPictureSectionDialog(); } + + updateCursorPosition(event: KeyboardEvent | MouseEvent): void { + if (event.target) { + const textarea = event.target as HTMLTextAreaElement; + + const positionStart = textarea.selectionStart; + const positionEnd = textarea.selectionEnd; + + const selectedCharacterCount = positionEnd - positionStart; + console.log(`cursor position updated: [${positionStart}, ${positionEnd}] (${selectedCharacterCount})`); + this.publicationEditionService.editCursorPosition(positionStart, positionEnd); + } + + } } 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 8a4648f..f1e24a5 100644 --- a/frontend/src/app/pages/publication-edition/publication-edition.service.ts +++ b/frontend/src/app/pages/publication-edition/publication-edition.service.ts @@ -9,6 +9,22 @@ import { copy } from '../../core/utils/ObjectUtils'; import { MatDialog } from "@angular/material/dialog"; import { PictureSelectionDialog } from "./picture-selection-dialog/picture-selection-dialog.component"; +export class CursorPosition { + start: number; + end: number; + selectedCharacters: number; + + constructor(start: number, end: number) { + this.start = start; + this.end = end; + this.selectedCharacters = end - start; + } +} + +export interface PublicationEditionState { + publication: Publication; + cursorPosition: CursorPosition; +} const DEFAULT_PUBLICATION: Publication = { id: '', @@ -27,6 +43,13 @@ const DEFAULT_PUBLICATION: Publication = { } }; +const DEFAULT_CURSOR_POSITION = new CursorPosition(0, 0); + +const DEFAULT_STATE: PublicationEditionState = { + publication: DEFAULT_PUBLICATION, + cursorPosition: DEFAULT_CURSOR_POSITION +}; + @Injectable() export class PublicationEditionService implements OnDestroy { private readonly activatedRoute = inject(ActivatedRoute); @@ -37,7 +60,7 @@ export class PublicationEditionService implements OnDestroy { private readonly dialog = inject(MatDialog); private isLoadingSubject = new BehaviorSubject(false); - private publicationSubject = new BehaviorSubject(copy(DEFAULT_PUBLICATION)); + private stateSubject = new BehaviorSubject(copy(DEFAULT_STATE)); private subscriptions: Subscription[] = []; private isSavingSubject = new BehaviorSubject(false); @@ -45,12 +68,12 @@ export class PublicationEditionService implements OnDestroy { this.subscriptions.forEach(subscription => subscription.unsubscribe()); } - private get _publication(): Publication { - return this.publicationSubject.value; + private get _state(): PublicationEditionState { + return this.stateSubject.value; } - private _save(publication: Publication): void { - this.publicationSubject.next(publication); + private _save(state: PublicationEditionState): void { + this.stateSubject.next(state); } get isLoading$(): Observable { @@ -61,8 +84,8 @@ export class PublicationEditionService implements OnDestroy { return this.isSavingSubject.asObservable(); } - get publication$(): Observable { - return this.publicationSubject.asObservable(); + get state$(): Observable { + return this.stateSubject.asObservable(); } loadPublication(): void { @@ -76,7 +99,9 @@ export class PublicationEditionService implements OnDestroy { } else { this.publicationRestService.getById(publicationId) .then(publication => { - this.publicationSubject.next(publication); + const state = this._state; + state.publication = publication; + this.stateSubject.next(state); }) .catch(error => { const errorMessage = 'A technical error occurred while loading publication data.'; @@ -89,27 +114,27 @@ export class PublicationEditionService implements OnDestroy { } editTitle(title: string): void { - const publication = this._publication; - publication.title = title; - this._save(publication); + const state = this._state; + state.publication.title = title; + this._save(state); } editDescription(description: string): void { - const publication = this._publication; - publication.description = description; - this._save(publication); + const state = this._state; + state.publication.description = description; + this._save(state); } editText(text: string): void { - const publication = this._publication; - publication.text = text; - this._save(publication); + const state = this._state; + state.publication.text = text; + this._save(state); } editIllustrationId(pictureId: string): void { - const publication = this._publication; - publication.illustrationId = pictureId - this._save(publication); + const state = this._state; + state.publication.illustrationId = pictureId + this._save(state); } displayPictureSectionDialog(): void { @@ -124,11 +149,55 @@ export class PublicationEditionService implements OnDestroy { this.subscriptions.push(afterDialogCloseSubscription); } + editCursorPosition(positionStart: number, positionEnd: number): void { + const state = this._state; + + state.cursorPosition.start = positionStart; + state.cursorPosition.end = positionEnd; + + this._save(state); + } + + insertTitle(titleNumber: number): void { + if (titleNumber >= 1 && titleNumber <= 3) { + const state = this._state; + + const publication = state.publication; + + const publicationTextLeftPart = publication.text.substring(0, state.cursorPosition.start); + const publicationTextMiddlePart = publication.text.substring(state.cursorPosition.start, state.cursorPosition.end); + const publicationTextRightPart = publication.text.substring(state.cursorPosition.end); + const textWithTags = `${publicationTextLeftPart}[h${titleNumber}]${publicationTextMiddlePart}[/h${titleNumber}]${publicationTextRightPart}`; + + publication.text = textWithTags; + + this._save(state); + } else { + console.error(`Bad value for parameter of function 'insertTitle': '${titleNumber}'.`); + } + } + + insertLink(): void { + const state = this._state; + + const publication = state.publication; + + const publicationTextLeftPart = publication.text.substring(0, state.cursorPosition.start); + const publicationTextMiddlePart = publication.text.substring(state.cursorPosition.start, state.cursorPosition.end); + const publicationTextRightPart = publication.text.substring(state.cursorPosition.end); + const textWithTags = `${publicationTextLeftPart}[link href="" txt="${publicationTextMiddlePart}" /]${publicationTextRightPart}`; + + publication.text = textWithTags; + + this._save(state); + } + + save(): void { - const publication = this._publication; + const state = this._state; this.isSavingSubject.next(true); - this.publicationRestService.update(publication) + this.publicationRestService.update(state.publication) .then(() => { this.snackBar.open('Publication updated succesfully!', 'Close', { duration: 5000 }); this.router.navigate(['/home']);