All files / src/app/core/services jobs-list-options.service.ts

55.55% Statements 55/99
41.86% Branches 18/43
40.9% Functions 9/22
85% Lines 51/60

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 1442x 2x 2x   2x 2x     4x         2x     9x       9x   9x   9x 3x           3x           3x 3x   3x 3x   3x 3x 3x               3x   3x   1x     3x 2x     3x         3x         1x       6x               2x                 2x 2x                     4x 4x 4x   4x 3x 3x 2x       4x 1x 1x 1x       4x   3x 3x 3x     1x       6x   6x     6x    
import { effect, Injectable, Signal, signal, WritableSignal } from '@angular/core';
import { DataStorageService } from './data-storage.service';
import { JobsListOptions } from '../model/jobs-list-options';
import { UserSummary } from '../model/user-summary.interface';
import { JobStatus, JobStatusMeta } from '../model/job.interface';
import { UserService } from './user.service';
 
// storage key for list options
const JOBS_FILTER_KEY = 'jobs-filter';
 
@Injectable({
  providedIn: 'root'
})
export class JobsListOptionsService {
 
  // first load is specific, we have to know if it has been done
  private firstLoadDone = false;
  // current list options 
  private jobsListOptions!: JobsListOptions;
  // every change of options result in a signal update
  private readonly jobsListOptionsSignal: WritableSignal<JobsListOptions | null> = signal(null); 
 
  constructor(private readonly userService: UserService, private readonly dataStorageService: DataStorageService) { 
    // a UserSummary change triggers a JobsListOptions change
    effect(() => {
      const userSummary = this.userService.getUserSummary()();
 
      // null summary only implies that the loading hasn't been done yet
      // we do nothing for now because we make sure to get a value in the UserService :
      // in case of a loading error it will be false
      // if loading is ok, then userSummary will be a UserSummary instance
      Iif(null === userSummary) {
        // wait for the summary to load
        return;
      }
 
      // forceReload will be used to force a list reload by setting forceReload(true) on the options
      let forceReload = false;
      if(!this.firstLoadDone) {
        // on first load, get options from storage, default to empty options
        const storedOptions = this.dataStorageService.getItem<JobsListOptions>(JOBS_FILTER_KEY);
        let newOptions = null === storedOptions ? new JobsListOptions() : Object.assign(new JobsListOptions(), storedOptions);
        // no need to force reloading when loading options from storage, as it is done before any jobs list loading
        newOptions.forceReload(null);
        this.jobsListOptions = newOptions;
        this.firstLoadDone = true;
      }
      // when summary changes after first load it may indicate a change in the jobs list, whether it's filtered or not
      // therefore we have to reload the list
      else E{
        forceReload = true;
      }
 
      let result = null;
      // summary may be false in case of a loading error
      if(userSummary !== false) {
        // checking options against user's summary
        result = this.checkOptions(userSummary);
      }
      // fallback to current options if needed
      if(!(result instanceof JobsListOptions)) {
        result = Object.assign(new JobsListOptions(), this.jobsListOptions);
      }
 
      Iif(forceReload) {
        result.forceReload(true);
        result.changePagination(0, null);
      }
 
      this.save(result);
    })
  }
 
  public clear() :void {
    this.save(new JobsListOptions());
  }
 
  public getCurrentOptions(): JobsListOptions {
    return this.jobsListOptions;
  }
 
  public getJobsListOptions(): Signal<JobsListOptions | null> {
    return this.jobsListOptionsSignal.asReadonly();
  }
 
  private copyOptions() :JobsListOptions {
    return Object.assign(new JobsListOptions, this.jobsListOptions);
  }
 
  public changePagination(page: number, itemsPerPage: number | null): void {
    const newOptions = this.copyOptions();
    this.save(newOptions.changePagination(page, itemsPerPage));
  }
 
  public filter(status: string | null, statusMeta: string | null): void {
    const newOptions = this.copyOptions();
    this.save(newOptions.filter(status, statusMeta));
  }
 
  public sort(sort: string): void {
    const newOptions = this.copyOptions();
    this.save(newOptions.sort(sort));
  }
 
  // checks if current options are compatible with a UserSummary
  // and updates filters if necessary
  public checkOptions(summary: UserSummary): JobsListOptions | null {
    let removeStatus, removeStatusMeta = false;
    const currentStatus = this.jobsListOptions.getStatus();
    const currentStatusMeta = this.jobsListOptions.getStatusMeta();
 
    if(currentStatus !== null) {
      const status = JobStatus[currentStatus as keyof typeof JobStatus];
      if(!summary.jobStatuses[status] || summary.jobStatuses[status] === null) {
        removeStatus = true;
      }
    }
    
    if(currentStatusMeta !== null) {
      const statusMeta = JobStatusMeta[currentStatusMeta as keyof typeof JobStatusMeta];
      if(!summary.usableJobStatusMetas.includes(statusMeta)) {
        removeStatusMeta = true;
      }
    }
 
    if(removeStatus || removeStatusMeta) {
      // new options should reflect previous one, only status and/or statusMeta will be removed
      const newOptions = Object.assign(new JobsListOptions(), this.jobsListOptions);
      newOptions.filter(removeStatus ? null : currentStatus, removeStatusMeta ? null : currentStatusMeta);
      return newOptions;
    }
 
    return null;
  }
 
  private save(options: JobsListOptions) :void {
    this.jobsListOptions = options;
    // create a new instance so that the signal detects the change
    this.jobsListOptionsSignal.set(this.jobsListOptions);
    
    // store in the data storage for later
    this.dataStorageService.setItem(JOBS_FILTER_KEY, this.jobsListOptions);
  }
}