All files / app/services/image image.service.ts

100% Statements 38/38
100% Branches 29/29
100% Functions 8/8
100% Lines 38/38

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167                                  1x         129x       129x                 129x   129x   129x                     278x   90x   90x 90x 90x   188x                     46x   12x   12x 12x 12x   34x                 324x                 318x                     297x 303x           269x 269x 6x   263x 263x 263x   269x                                     65x 65x 71x         37x 37x 37x 2x     37x 37x                        
import { PreloaderService } from './../preloader/preloader.service';
import { Inject, Injectable } from '@angular/core';
import { LogService } from '../log/log.service';
import { Preloaders } from '../preloader/preloaders/preloaders';
import { ENV } from 'src/environments/injectionToken/environment-provider';
import { IEnvironment } from 'src/environments/interface/ienvironment';
 
/**
 * Service used to notify {@link Preloaders} that some images are loading, and
 * that some images are finished loading (whether the image has successfully
 * loaded or an error occured).
 *
 * Inspired by https://dev.to/paviad/angular-wait-for-all-images-to-load-3hp1
 */
@Injectable({
  providedIn: 'root',
})
export class ImageService {
  /**
   * Map used to track the images to the associated {@link Preloaders}, to avoid
   * having a image load multiple times for the same preloader.
   */
  private images: Map<HTMLElement, Map<Preloaders, boolean>> = new Map();
  /** Logger. See {@link LogService} */
  logger: LogService;
  /** Keeps track of loaded images. */
  private loadedImages: Map<HTMLElement, boolean> = new Map();
 
  /**
   * Image service constructor
   *
   * @param preloaderService The {@link PreloaderService}
   * @param logService The {@link LogService}
   */
  constructor(
    private preloaderService: PreloaderService,
    logService: LogService,
    @Inject(ENV) private environment: IEnvironment
  ) {
    this.logger = logService.withClassName('ImageService');
  }
 
  /**
   * Default to load message, if none is specified
   *
   * @param img The image that has to load
   * @param loaders The {@link Preloaders}
   * @returns The message
   */
  private imgToLoadMessage(img: HTMLElement, loaders: Preloaders[]): string {
    if (!this.environment.production && this.environment.fullLoadingMessages) {
      const src = (
        img.getAttribute('src') ? img.getAttribute('src') : 'no file'
      ) as string;
      const split = src.split('/');
      const fileName = split[split.length - 1];
      return 'Loading img - ' + fileName + ' - ' + loaders;
    }
    return 'Loading image...';
  }
 
  /**
   * Default loaded message, if none is specified
   *
   * @param img The image that has to load
   * @param loaders The {@link Preloaders}
   * @returns The message
   */
  private imgLoadedMessage(img: HTMLElement, loaders: Preloaders[]): string {
    if (!this.environment.production && this.environment.fullLoadingMessages) {
      const src = (
        img.getAttribute('src') ? img.getAttribute('src') : 'no file'
      ) as string;
      const split = src.split('/');
      const fileName = split[split.length - 1];
      return 'Img loaded - ' + fileName + ' - ' + loaders;
    }
    return 'Loading image...';
  }
 
  /**
   * Whether or not the preloader tot included in the preloader message.
   *
   * @returns The boolean
   */
  private imgMessageWithPreloaderTot(): boolean {
    return !this.environment.production && this.environment.fullLoadingMessages;
  }
  /**
   * Whether or not the preloader tot included in the message for the totality
   * of preloaders.
   *
   * @returns The boolean
   */
  private imgMessageWithTot(): boolean {
    return !this.environment.production && this.environment.fullLoadingMessages;
  }
 
  /**
   * Notifies {@link Preloaders}, if it hasn't already been done, that an image
   * has to load.
   *
   * @param img The image that has to load
   * @param loaders The {@link Preloaders}
   */
  imageLoading(img: HTMLElement, loaders: Preloaders[]) {
    for (const loader of loaders) {
      if (
        (!this.images.has(img) ||
          !this.images.get(img)?.has(loader) ||
          !this.images.get(img)?.get(loader)) &&
        (!this.loadedImages.has(img) || !this.loadedImages.get(img))
      ) {
        this.loadedImages.set(img, false);
        if (this.images.get(img)) {
          this.images.get(img)?.set(loader, true);
        } else {
          const mapToSet = new Map<Preloaders, boolean>();
          mapToSet.set(loader, true);
          this.images.set(img, mapToSet);
        }
        this.preloaderService.toLoad(
          loader,
          1,
          this.imgToLoadMessage(img, loaders),
          this.imgMessageWithPreloaderTot(),
          this.imgMessageWithTot()
        );
      }
    }
  }
 
  /**
   * Notifies {@link Preloaders}, if it hasn't already been done, that an image
   * has loaded.
   *
   * @param img The image that has loaded
   * @param loaders The {@link Preloaders}
   */
  imageLoadedOrError(img: HTMLElement, loaders: Preloaders[]) {
    this.loadedImages.set(img, true);
    for (const loader of loaders) {
      if (
        this.images.has(img) &&
        this.images.get(img)?.has(loader) &&
        this.images.get(img)?.get(loader)
      ) {
        this.images.get(img)?.set(loader, false);
        let timeout = 0;
        if (!this.environment.production && !this.environment.isTesting)
          timeout =
            Math.random() * this.environment.artificialRandomLoadingTime +
            this.environment.artificialMinLoadingTime;
        setTimeout(() => {
          this.preloaderService.loaded(
            loader,
            1,
            this.imgLoadedMessage(img, loaders),
            this.imgMessageWithPreloaderTot(),
            this.imgMessageWithTot()
          );
        }, timeout);
      }
    }
  }
}