import { Component, OnInit, OnDestroy, inject, TemplateRef, ViewChild, ViewContainerRef, Renderer2, ElementRef, } from '@angular/core'; import { Editor } from 'ngx-editor'; import { DateAdapter } from '@angular/material/core'; import { JournalEntry } from 'src/interfaces/interfaces'; import localeDe from '@angular/common/locales/de'; import { registerLocaleData } from '@angular/common'; import { DataService } from 'src/services/data/data.service'; import { TooltipService } from 'src/services/tooltip/tooltip.service'; import { HighlightComponent } from 'src/app/shared-components/highlight/highlight.component'; @Component({ selector: 'app-journal-notes', templateUrl: './journal-notes.component.html', styleUrl: './journal-notes.component.scss', }) export class JournalNotesComponent implements OnInit, OnDestroy { /** Reference to the tooltip */ @ViewChild('tooltip', { read: TemplateRef }) tooltip!: TemplateRef; /** Reference to the creation anchor */ @ViewChild('creationAnchor', { read: ViewContainerRef }) creationAnchor!: ViewContainerRef; editor: Editor = new Editor(); toolbar: any = [ // default value ['bold', 'italic'], ['bullet_list'], [{ heading: ['h3', 'h4', 'h5', 'h6'] }], ]; public entries: JournalEntry[] = []; public currentEntry: JournalEntry = { title: '', content: '', created: new Date(), }; public isInEditMode = false; public currentIndex: number = -1; private backupIndex: number = -1; public isNewEntry = false; public tooltipText: string = ''; public npcDescriptions: any = {}; tooltipifiedEntry: JournalEntry = { title: 'Title', content: 'Content', created: new Date(), }; private _adapter: DateAdapter = inject(DateAdapter); private dataService: DataService = inject(DataService); private tooltipService: TooltipService = inject(TooltipService); private renderer: Renderer2 = inject(Renderer2); private el: ElementRef = inject(ElementRef); ngOnInit(): void { registerLocaleData(localeDe); this._adapter.setLocale('de'); this.entries = this.dataService.notesData; // if the list is empty, set the currentIndex to -1 to hide the entry-container if (this.entries.length === 0) { this.currentIndex = -1; } else { this.selectEntry(0); } } // ACTIONS FROM THE TEMPLATE /** * Sets the currentEntry variable when being clicked on in the entries list on the left. * It also modifies the content of the entry by highlighting names and adding tooltips. * Is only called when the entry is not in edit mode or a different entry is selected. * @param index The index of the selected entry. */ public selectEntry(index: number): void { // hier if (this.isInEditMode || index !== this.currentIndex) { this.currentIndex = index; this.currentEntry = this.getEntryAt(index); this.isNewEntry = false; this.isInEditMode = false; this.tooltipify(); } } /** * Adds an new empty entry, switches to edit mode and removes the highlighting of the selected entry. */ public addEntry(): void { this.currentEntry = { title: '', content: '', created: new Date(), }; this.isNewEntry = true; this.isInEditMode = true; this.backupIndex = this.currentIndex; // Hightlight no entry because the placeholder is shown as active this.currentIndex = -1; } /** * Switches to edit mode. */ public editEntry(): void { this.isInEditMode = true; } /** * If the current entry is a new entry, it will be prepended to the entries list. * Else it is saved at the current index. * The content is the tooltipified and saved in the database. */ public saveEntry(): void { if (this.isNewEntry) { // Prepend the new JournalEntry this.entries.unshift(this.currentEntry); this.isNewEntry = false; this.currentIndex = 0; } this.isInEditMode = false; this.entries[this.currentIndex] = this.currentEntry; this.tooltipify(); this.uploadNotes(); } /** * Discards the current entry and resets the currentEntry to the last saved version. * If the entry was a new entry, the currentIndex is reset to the last selected entry. * The content is also tooltipified again. */ public discardEntry(): void { if (this.isNewEntry) { this.currentIndex = this.backupIndex; this.isNewEntry = false; } if (this.entries.length > 0) { // Reset the currentEntry to the last saved version this.currentEntry = this.getEntryAt(this.currentIndex); } this.isInEditMode = false; this.tooltipify(); } /** * Deletes the current entry and removes it from the entries list. * If the last entry was deleted, the currentIndex is reset to -1. * The content is saved in the database. */ public deleteEntry(): void { this.entries.splice(this.currentIndex, 1); if (this.entries.length === 0) { this.currentIndex = -1; } else { this.currentIndex = Math.max(this.currentIndex - 1, 0); this.currentEntry = this.getEntryAt(this.currentIndex); // Update the tooltipified entry this.tooltipify(); } this.uploadNotes(); } /** * Returns a deep copy of the entry at the given index. * @param index Defines the index of the entry that should be returned. * @returns A deep copy of the entry at the given index. */ private getEntryAt(index: number): JournalEntry { return JSON.parse(JSON.stringify(this.entries[index])); } /** * Saves the current entries in the data service. */ private uploadNotes(): void { this.dataService.notesData = this.entries; } /** * Converts the content of the current entry to a tooltipified version. * It then adds the highlights to the names and sets the tooltip text. * Is called on every refresh of an entry (selecting, saving, discarding, deleting, adding, etc.) */ public tooltipify(): void { let result: any = this.tooltipService.tooltipifyEntry( this.getEntryAt(this.currentIndex).content, ); this.tooltipifiedEntry.content = result.content; this.npcDescriptions = result.npcDescriptions; setTimeout(() => { result.npcs.forEach((name: string) => { this.addHighlightsToText(name); }); }); } /** * Adds a highlight component to the names that are mentioned in the current entry. * Highlights and adds tooltips to the names that are mentioned in the current entry. * @param name The name of the person which is currently highlighted. */ private addHighlightsToText(name: string) { // get all elements where a highlight component should be added to const parent = this.el.nativeElement.querySelectorAll('.' + name); parent.forEach((element: any) => { const componentRef = this.creationAnchor.createComponent(HighlightComponent); componentRef.instance.text = name; componentRef.instance.tooltip = this.tooltip; this.renderer.appendChild(element, componentRef.location.nativeElement); // add a mouseover event listener to the highlight component, when it is hovered over, the tooltip-text is set this.renderer.listen( componentRef.location.nativeElement, 'mouseover', () => { this.tooltipText = this.npcDescriptions[name]; }, ); }); } ngOnDestroy(): void { this.editor.destroy(); } }