import { EventEmitter, Injectable } from '@angular/core'; import { Context } from '../../interfaces/context/context'; import { ContextConfiguration } from '../../interfaces/context/context-configuration'; import { NavState } from '../../enums/nav-state'; import { PersistenceService } from '../persistence/persistence.service'; import { UiService } from '../ui/ui.service'; import { StringExService } from '../string-ex/string-ex.service'; /** * Context service. */ @Injectable({ providedIn: 'root' }) export class ContextService { /** Persistence key. */ private get persistenceKey() { return { contexts: 'contexts', selectedContext: 'selectedContext', }; } /* Contexts. */ #contexts?: Context[]; /* Contexts getter. */ public get contexts() { // cache the get's if (this.#contexts) { return this.#contexts; } // retrieve the value const value = JSON.parse(this.persistenceService.getItem(this.persistenceKey.contexts) ?? JSON.stringify([this.emptyContext])); // cache the value retrieved this.#contexts = value; // return the value return value; } /* Contexts setter. */ public set contexts(value: Context[]) { // cache the value to store this.#contexts = value; // store the value if (value) { this.persistenceService.setItem(this.persistenceKey.contexts, JSON.stringify(value)); } } /** Empty context. */ private readonly emptyContext = { id: 0, name: '', storage: {} as Storage }; /** Selected context getter. */ public get selectedContext() { // serialize as id const selectedContext = this.persistenceService.getItem(this.persistenceKey.selectedContext); if (!selectedContext) { return undefined; } const selectedContextId = Number.parseInt(selectedContext, 10); const context = this.contexts.find((_) => _.id === selectedContextId); return context; } /** Selected context setter. */ public set selectedContext(value: Context | undefined) { // save context this.copyStorage(this.persistenceService.storage, this.selectedContext?.storage); // serialize as id if (typeof value === 'undefined') { this.persistenceService.removeItem(this.persistenceKey.selectedContext); } else { this.persistenceService.setItem(this.persistenceKey.selectedContext, value.id.toString()); } // persist contexts this.contexts = this.contexts; // retrieve context this.copyStorage(value?.storage, this.persistenceService.storage); } /** Whether context switcher is in editing mode. */ public isEditing = false; /** Nav state "context state" persistence key. */ public readonly navStatePersistenceKey = 'navState'; /** Nav state getter. */ public get navState() { return Number.parseInt(this.persistenceService.getItem(this.navStatePersistenceKey) ?? NavState.Closed.toString(), 10); } /** Nav state setter. */ public set navState(value) { this.persistenceService.setItem(this.navStatePersistenceKey, value.toString()); // emit change event const navStateConfiguration = this.navStateConfigurations[this.navState]; this.navStateChanged$.emit(navStateConfiguration); } /** Nav state changed event emitter. */ public readonly navStateChanged$ = new EventEmitter(); /** Nav state configurations */ public get navStateConfigurations(): ContextConfiguration[] { return [ { width: '10px', backgroundColor: 'rgba(0,0,0,0)', name: () => '' }, { width: '350px', backgroundColor: 'rgba(0,0,0,0.2)', name: (item: Context) => item.name }, { width: '100px', backgroundColor: 'rgba(0,0,0,0.1)', name: (item: Context) => StringExService.glyph(item.name) }, ]; } /** * Constructs the entities service. * ~constructor * * @param uiService The ui service injected dependency. * @param persistenceService The persistence service injected dependency. */ constructor( public readonly uiService: UiService, public readonly persistenceService: PersistenceService, ) { } /** Context comparer. */ public contextEquals(context1?: Context, context2?: Context) { return context1?.id === context2?.id; } /** Copy relevant storage between switcher and persistence */ private copyStorage(source?: Storage, destination?: Storage) { if (typeof source !== 'undefined') { const { contexts, selectedContext, navState, ...serializabeStorage } = source; if (typeof destination !== 'undefined') { Object.entries(serializabeStorage).forEach((entry) => { destination[entry[0]] = entry[1]; }); } } } /** Get context caption */ public getCaption(item: Context) { const name = this.navStateConfigurations[this.navState].name(item) || '‣'; return this.contextEquals(item, this.selectedContext) ? name.toUpperCase() : name; } /** Get context tooltip title */ public getTitle(item: Context) { let title = ''; if (!this.isEditing) { title += this.navStateConfigurations[NavState.Open].name(item); if (this.navState === NavState.Open) { title += '\n\n' + this.uiService.uiText('Click to edit.'); } } return title; } }