project.ts 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. import { Component, inject } from '@angular/core';
  2. import { Router, ActivatedRoute } from '@angular/router';
  3. import { PlatformLocation } from '@angular/common';
  4. import { Title, Meta } from '@angular/platform-browser';
  5. import { ProjectService } from '../service/project-service';
  6. import { TranslateService, _ } from '@ngx-translate/core';
  7. import { UtilService } from '../service/util-service';
  8. import { ProjectModel } from '../model/project';
  9. import { environment } from '../../environments/environment';
  10. @Component({
  11. selector: 'app-project', templateUrl: './project.html', styleUrls: ['./project.scss']
  12. })
  13. export class Project {
  14. project: ProjectModel | undefined;
  15. apiURL: string = environment.apiUrl;
  16. utilService: UtilService;
  17. private galleryIndexMap = new Map<string, number>();
  18. private translate = inject(TranslateService)
  19. /**
  20. * Index of the photo currently on the gallery.
  21. */
  22. private curIndex: number = -1;
  23. /**
  24. * Total number of images.
  25. */
  26. private maxIndex: number = -1;
  27. constructor(
  28. private route: ActivatedRoute, private projectService: ProjectService, private router: Router,
  29. private titleService: Title, private metaService: Meta,
  30. private platformLocation: PlatformLocation
  31. ){
  32. this.utilService = new UtilService();
  33. }
  34. ngOnInit(): void {
  35. const id = this.route.snapshot.paramMap.get('id');
  36. // Use the id to fetch data or perform actions
  37. this.projectService.getProject("" + id).subscribe(
  38. data => {
  39. this.project = data;
  40. if (this.project.images.length === 0) {
  41. for (const child of this.project.children) {
  42. const childFirstImage = child.images[0];
  43. if (childFirstImage) this.project.images.push(childFirstImage);
  44. }
  45. }
  46. this.buildGalleryIndexMap();
  47. // Set meta tags
  48. const siteUrl: string = this.platformLocation.protocol + "//" + this.platformLocation.hostname;
  49. const projectUrl: string = siteUrl + '/projects' + this.project.permalink;
  50. this.metaService.addTag({ property: 'canonical', content: projectUrl });
  51. this.metaService.addTag({ property: 'og:url', content: projectUrl });
  52. this.metaService.addTag({ property: 'og:description', content: this.project.description});
  53. this.metaService.addTag({ property: 'description', content: this.project.description});
  54. if (this.project.images.length > 0)
  55. this.metaService.addTag({ property: 'og:image', content: this.apiURL + this.project.images[0].path });
  56. else this.metaService.addTag({ property: 'og:image', content: siteUrl + '/img/logo/leather.png' });
  57. this.translate.get(_('SITE.TITLE')).subscribe((res: string) => {
  58. this.titleService.setTitle(this.project?.title + " - " + res);
  59. this.metaService.addTag({ property: 'title', content: this.project?.title + " - " + res });
  60. this.metaService.addTag({ property: 'og:title', content: this.project?.title + " - " + res });
  61. });
  62. },
  63. err => { this.router.navigate(['/error']); }
  64. );
  65. }
  66. private buildGalleryIndexMap(): void {
  67. this.galleryIndexMap.clear();
  68. let index = 1;
  69. if (!this.project) {
  70. this.maxIndex = 0;
  71. return;
  72. }
  73. for (const image of this.project.images)
  74. this.galleryIndexMap.set(this.getProjectImageKey(image.id), index++);
  75. for (const child of this.project.children ?? []) {
  76. for (const image of child.images ?? [])
  77. this.galleryIndexMap.set(this.getChildImageKey(child.id, image.id), index++);
  78. }
  79. this.maxIndex = index - 1;
  80. }
  81. private getProjectImageKey(imageId: number): string {
  82. return 'project:' + imageId;
  83. }
  84. private getChildImageKey(childId: number, imageId: number): string {
  85. return 'child:' + childId + ':' + imageId;
  86. }
  87. getGalleryIndexForProjectImage(imageId: number): number {
  88. return this.galleryIndexMap.get(this.getProjectImageKey(imageId)) ?? -1;
  89. }
  90. getGalleryIndexForChildImage(childId: number, imageId: number): number {
  91. return this.galleryIndexMap.get(this.getChildImageKey(childId, imageId)) ?? -1;
  92. }
  93. /**
  94. * Opens the gallery.
  95. *
  96. * @param index Index of the photo to display
  97. */
  98. galleryOpen(index: any){
  99. const parsedIndex = Number(index);
  100. let cover: HTMLDivElement = <HTMLDivElement> document.getElementById('gallery-cover');
  101. let gallery: HTMLDivElement = <HTMLDivElement> document.getElementById('gallery');
  102. if (Number.isNaN(parsedIndex)) {
  103. console.error('Invalid image index: ' + index + '/' + this.maxIndex);
  104. return;
  105. }
  106. cover.style.display = 'block';
  107. cover.style.opacity = '0.6';
  108. gallery.style.display = 'flex';
  109. gallery.style.opacity = '1';
  110. if (parsedIndex >= 1 && parsedIndex <= this.maxIndex){
  111. this.curIndex = parsedIndex;
  112. this.gallerySet();
  113. }
  114. else console.error('Invalid image index: ' + parsedIndex + '/' + this.maxIndex);
  115. }
  116. /**
  117. * Closes the gallery.
  118. */
  119. galleryClose(){
  120. let cover: HTMLDivElement = <HTMLDivElement> document.getElementById('gallery-cover');
  121. let gallery: HTMLDivElement = <HTMLDivElement> document.getElementById('gallery');
  122. let video: HTMLVideoElement = <HTMLVideoElement> document.getElementById('gallery-video');
  123. cover.style.display = 'none';
  124. cover.style.opacity = '0';
  125. gallery.style.display = 'none';
  126. gallery.style.opacity = '0';
  127. video.pause();
  128. video.currentTime = 0;
  129. }
  130. private delay(ms: number){ return new Promise(resolve => setTimeout(resolve, ms)); }
  131. /**
  132. * Pupulates the data in the gallery.
  133. */
  134. async gallerySet(){
  135. var fade = <HTMLDivElement> document.getElementById('gallery-fade');
  136. fade.style.display = 'block';
  137. fade.style.opacity = '1';
  138. await this.delay(200);
  139. const activeImage = document.getElementById('img-' + this.curIndex);
  140. const imageTitle = activeImage?.getAttribute('alt') || '';
  141. const childTitle = activeImage?.dataset['childTitle'] || '';
  142. let titleText = '';
  143. if (childTitle.length > 0) titleText += ' - ' + childTitle;
  144. if (imageTitle.length > 0) titleText += ': ' + imageTitle;
  145. let title: HTMLSpanElement = <HTMLSpanElement> document.getElementById('gallery-title');
  146. let text: HTMLParagraphElement = <HTMLParagraphElement> document.getElementById('gallery-text');
  147. let image: HTMLImageElement = <HTMLImageElement> document.getElementById('gallery-img');
  148. let video: HTMLVideoElement = <HTMLVideoElement> document.getElementById('gallery-video');
  149. title.innerHTML = titleText;
  150. if (document.getElementById('img-' + this.curIndex)?.dataset["video"] == 'true'){
  151. image.style.display = 'none';
  152. video.style.display = 'block';
  153. video.setAttribute("src", document.getElementById('img-' + this.curIndex)?.getAttribute("src") || "");
  154. }
  155. else{
  156. video.pause();
  157. video.currentTime = 0;
  158. video.style.display = 'none';
  159. image.style.display = 'block';
  160. image.src = document.getElementById('img-' + this.curIndex)?.getAttribute("src") || "";
  161. image.srcset = document.getElementById('img-' + this.curIndex)?.getAttribute("srcset") || "";
  162. }
  163. let description: string = document.getElementById('img-' + this.curIndex)?.dataset["description"] || "";
  164. text.innerHTML = description;
  165. if (description.length > 0) text.style.display = 'block';
  166. else text.style.display = 'none';
  167. fade.style.opacity = '0';
  168. await this.delay(200);
  169. fade.style.display = 'none';
  170. }
  171. /**
  172. * Displays the next photo in the gallery.
  173. */
  174. galleryNext(){
  175. this.curIndex ++;
  176. if (this.curIndex >= this.maxIndex) this.curIndex = 1;
  177. this.gallerySet();
  178. }
  179. /**
  180. * Displays the previous photo in the gallery.
  181. */
  182. galleryPrev(){
  183. this.curIndex --;
  184. if (this.curIndex < 1) this.curIndex = this.maxIndex;
  185. this.gallerySet();
  186. }
  187. private swipeCoord?: [number, number];
  188. private swipeTime?: number;
  189. gallerySwipe(e: TouchEvent, when: string): void {
  190. const coord: [number, number] = [e.changedTouches[0].pageX, e.changedTouches[0].pageY];
  191. const time = new Date().getTime();
  192. if (when === 'start') {
  193. this.swipeCoord = coord;
  194. this.swipeTime = time;
  195. }
  196. else if (this.swipeCoord && this.swipeTime && when === 'end') {
  197. const direction = [coord[0] - this.swipeCoord[0], coord[1] - this.swipeCoord[1]];
  198. const duration = time - this.swipeTime;
  199. if (duration < 1000 && Math.abs(direction[0]) > 30 && Math.abs(direction[0]) > Math.abs(direction[1] * 3)){
  200. if (direction[0] < 0) this.galleryNext();
  201. else this.galleryPrev();
  202. }
  203. }
  204. }
  205. }