journal-notes.component.ts 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. import {
  2. Component,
  3. OnInit,
  4. OnDestroy,
  5. inject,
  6. TemplateRef,
  7. ViewChild,
  8. ViewContainerRef,
  9. Renderer2,
  10. ElementRef,
  11. } from '@angular/core';
  12. import { Editor } from 'ngx-editor';
  13. import { DateAdapter } from '@angular/material/core';
  14. import { JournalEntry } from 'src/interfaces/interfaces';
  15. import localeDe from '@angular/common/locales/de';
  16. import { registerLocaleData } from '@angular/common';
  17. import { DataService } from 'src/services/data/data.service';
  18. import { TooltipService } from 'src/services/tooltip/tooltip.service';
  19. import { HighlightComponent } from 'src/app/shared-components/highlight/highlight.component';
  20. @Component({
  21. selector: 'app-journal-notes',
  22. templateUrl: './journal-notes.component.html',
  23. styleUrl: './journal-notes.component.scss',
  24. })
  25. export class JournalNotesComponent implements OnInit, OnDestroy {
  26. /** Reference to the tooltip */
  27. @ViewChild('tooltip', { read: TemplateRef }) tooltip!: TemplateRef<any>;
  28. /** Reference to the creation anchor */
  29. @ViewChild('creationAnchor', { read: ViewContainerRef })
  30. creationAnchor!: ViewContainerRef;
  31. editor: Editor = new Editor();
  32. toolbar: any = [
  33. // default value
  34. ['bold', 'italic'],
  35. ['bullet_list'],
  36. [{ heading: ['h3', 'h4', 'h5', 'h6'] }],
  37. ];
  38. public entries: JournalEntry[] = [];
  39. public currentEntry: JournalEntry = {
  40. title: '',
  41. content: '',
  42. created: new Date(),
  43. };
  44. public isInEditMode = false;
  45. public currentEntryIndex: number = 0;
  46. private backupIndex: number = -1;
  47. public isNewEntry = false;
  48. public tooltipText: string = '';
  49. public npcDescriptions: any = {};
  50. tooltipifiedEntry: JournalEntry = {
  51. title: 'Title',
  52. content: 'Content',
  53. created: new Date(),
  54. };
  55. private _adapter: DateAdapter<any> = inject(DateAdapter);
  56. private dataService: DataService = inject(DataService);
  57. private tooltipService: TooltipService = inject(TooltipService);
  58. private renderer: Renderer2 = inject(Renderer2);
  59. private el: ElementRef = inject(ElementRef);
  60. ngOnInit(): void {
  61. registerLocaleData(localeDe);
  62. this._adapter.setLocale('de');
  63. this.entries = this.dataService.notesData;
  64. // if the list is empty, set the currentEntryIndex to -1 to hide the entry-container
  65. if (this.entries.length === 0) {
  66. this.currentEntryIndex = -1;
  67. } else {
  68. this.currentEntry = this.entries[0];
  69. this.tooltipifiedEntry = JSON.parse(JSON.stringify(this.currentEntry));
  70. }
  71. this.tooltipify();
  72. }
  73. // ACTIONS FROM THE TEMPLATE
  74. /**
  75. * Sets the currentEntry variable when being clicked on in the entries list on the left.
  76. * It also modifies the content of the entry by highlighting names and adding tooltips.
  77. * Is only called when the entry is not in edit mode or a different entry is selected.
  78. * @param index The index of the selected entry.
  79. */
  80. public selectEntry(index: number): void {
  81. if (this.isInEditMode || index !== this.currentEntryIndex) {
  82. this.currentEntryIndex = index;
  83. this.currentEntry = this.getEntryAt(index);
  84. this.isNewEntry = false;
  85. this.isInEditMode = false;
  86. this.tooltipify();
  87. }
  88. }
  89. /**
  90. * Adds an new empty entry, switches to edit mode and removes the highlighting of the selected entry.
  91. */
  92. public addEntry(): void {
  93. this.currentEntry = {
  94. title: '',
  95. content: '',
  96. created: new Date(),
  97. };
  98. this.isNewEntry = true;
  99. this.isInEditMode = true;
  100. this.backupIndex = this.currentEntryIndex;
  101. // Hightlight no entry because the placeholder is shown as active
  102. this.currentEntryIndex = -1;
  103. }
  104. /**
  105. * Switches to edit mode.
  106. */
  107. public editEntry(): void {
  108. this.isInEditMode = true;
  109. }
  110. /**
  111. * If the current entry is a new entry, it will be prepended to the entries list.
  112. * Else it is saved at the current index.
  113. * The content is the tooltipified and saved in the database.
  114. */
  115. public saveEntry(): void {
  116. if (this.isNewEntry) {
  117. // Prepend the new JournalEntry
  118. this.entries.unshift(this.currentEntry);
  119. this.isNewEntry = false;
  120. this.currentEntryIndex = 0;
  121. }
  122. this.isInEditMode = false;
  123. this.entries[this.currentEntryIndex] = this.currentEntry;
  124. this.tooltipify();
  125. this.uploadNotes();
  126. }
  127. /**
  128. * Discards the current entry and resets the currentEntry to the last saved version.
  129. * If the entry was a new entry, the currentEntryIndex is reset to the last selected entry.
  130. * The content is also tooltipified again.
  131. */
  132. public discardEntry(): void {
  133. if (this.isNewEntry) {
  134. this.currentEntryIndex = this.backupIndex;
  135. this.isNewEntry = false;
  136. }
  137. if (this.entries.length > 0) {
  138. // Reset the currentEntry to the last saved version
  139. this.currentEntry = this.getEntryAt(this.currentEntryIndex);
  140. }
  141. this.isInEditMode = false;
  142. this.tooltipify();
  143. }
  144. /**
  145. * Deletes the current entry and removes it from the entries list.
  146. * If the last entry was deleted, the currentEntryIndex is reset to -1.
  147. * The content is saved in the database.
  148. */
  149. public deleteEntry(): void {
  150. this.entries.splice(this.currentEntryIndex, 1);
  151. if (this.entries.length === 0) {
  152. this.currentEntryIndex = -1;
  153. } else {
  154. this.currentEntryIndex = Math.max(this.currentEntryIndex - 1, 0);
  155. this.currentEntry = this.getEntryAt(this.currentEntryIndex);
  156. // Update the tooltipified entry
  157. this.tooltipify();
  158. }
  159. this.uploadNotes();
  160. }
  161. /**
  162. * Returns a deep copy of the entry at the given index.
  163. * @param index Defines the index of the entry that should be returned.
  164. * @returns A deep copy of the entry at the given index.
  165. */
  166. private getEntryAt(index: number): JournalEntry {
  167. return JSON.parse(JSON.stringify(this.entries[index]));
  168. }
  169. /**
  170. * Saves the current entries in the data service.
  171. */
  172. private uploadNotes(): void {
  173. this.dataService.notesData = this.entries;
  174. }
  175. /**
  176. * Converts the content of the current entry to a tooltipified version.
  177. * It then adds the highlights to the names and sets the tooltip text.
  178. * Is called on every refresh of an entry (selecting, saving, discarding, deleting, adding, etc.)
  179. */
  180. public tooltipify(): void {
  181. let result: any = this.tooltipService.tooltipifyEntry(
  182. this.getEntryAt(this.currentEntryIndex).content,
  183. );
  184. this.tooltipifiedEntry.content = result.content;
  185. this.npcDescriptions = result.npcDescriptions;
  186. setTimeout(() => {
  187. result.npcs.forEach((name: string) => {
  188. this.addHighlightsToText(name);
  189. });
  190. });
  191. }
  192. /**
  193. * Adds a highlight component to the names that are mentioned in the current entry.
  194. * Highlights and adds tooltips to the names that are mentioned in the current entry.
  195. * @param name The name of the person which is currently highlighted.
  196. */
  197. private addHighlightsToText(name: string) {
  198. // get all elements where a highlight component should be added to
  199. const parent = this.el.nativeElement.querySelectorAll('.' + name);
  200. parent.forEach((element: any) => {
  201. const componentRef =
  202. this.creationAnchor.createComponent(HighlightComponent);
  203. componentRef.instance.text = name;
  204. componentRef.instance.tooltip = this.tooltip;
  205. this.renderer.appendChild(element, componentRef.location.nativeElement);
  206. // add a mouseover event listener to the highlight component, when it is hovered over, the tooltip-text is set
  207. this.renderer.listen(
  208. componentRef.location.nativeElement,
  209. 'mouseover',
  210. () => {
  211. this.tooltipText = this.npcDescriptions[name];
  212. },
  213. );
  214. });
  215. }
  216. ngOnDestroy(): void {
  217. this.editor.destroy();
  218. }
  219. }