import _ from 'lodash' import log from 'loglevel' import { computed, makeObservable, observable } from 'mobx' import { DEFAULT_CONFIG } from '../config/default' import { IFileServiceImplementation, IGisServiceImplementation, } from '../interfaces' import { ApplicationModels } from '../interfaces/config.interface' import { IConnectivityServiceImplementation } from '../interfaces/connectivity.interface' import { IOfflineServiceImplementation } from '../interfaces/offline.interface' import { Element } from '../models' import { ConnectivityServiceProvider } from '../platform/connectivityServiceProvider' import { FileServiceProvider } from '../platform/fileServiceProvider' import { OfflineServiceProvider } from '../platform/offlineProvider' import { Files, Privileges, WorkRecords } from '../services' import { Api } from '../services/api' import { ApisauceInstance } from '../services/api/apisauce' import { Auth } from '../services/edocu/auth' import { Batch } from '../services/edocu/batch' import { Calendar } from '../services/edocu/calendar' import { Comments } from '../services/edocu/comments' import { Editors } from '../services/edocu/editors' import { Elements } from '../services/edocu/elements' import { Exports } from '../services/edocu/exports' import { Geojson } from '../services/edocu/geojson' import { Graph } from '../services/edocu/graph' import { History } from '../services/edocu/history' import { Iot } from '../services/edocu/iot' import { Listing } from '../services/edocu/listing' import { Notifications } from '../services/edocu/notifications' import { Organizations } from '../services/edocu/organizations' import { Registration } from '../services/edocu/registration' import { Tickets } from '../services/edocu/tickets' import { Translations } from '../services/edocu/translations' import { IMI } from '../services/imi/imi' import { Michalovce } from '../services/michalovce' import { BackendConfig } from '../setup.backend' import { ClientConfig } from '../setup.client' import { ChecklistStore } from './checklistStore' import { ElementStore } from './elementStore' import { GisStore } from './gisStore' import { ListingStore } from './listingStore' import { NotificationStore } from './notificationStore' import { OfflineStore } from './offlineStore' import { TranslationStore } from './translationStore' import { UserStore } from './userStore' export interface Stores { mainStore: MainStore userStore: UserStore elementStore: ElementStore gisStore: GisStore checklistStore: ChecklistStore offlineStore: OfflineStore notificationStore: NotificationStore listingStore: ListingStore translationStore: TranslationStore } export interface Services { api: Api comments: Comments auth: Auth registration: Registration elements: Elements batch: Batch editors: Editors graph: Graph iot: Iot listing: Listing exports: Exports organizations: Organizations tickets: Tickets geojson: Geojson notifications: Notifications translations: Translations history: History calendar: Calendar privileges: Privileges workRecords: WorkRecords files: Files imi: IMI } export class MainStore implements Services { api: Api log: log.Logger auth: Auth elements: Elements iot: Iot batch: Batch editors: Editors listing: Listing exports: Exports graph: Graph organizations: Organizations tickets: Tickets geojson: Geojson notifications: Notifications translations: Translations comments: Comments history: History calendar: Calendar workRecords: WorkRecords privileges: Privileges registration: Registration files: Files imi: IMI michalovce: Michalovce userStore: UserStore elementStore: ElementStore gisStore: GisStore offlineStore: OfflineStore notificationStore: NotificationStore listingStore: ListingStore translationStore: TranslationStore checklistStore: ChecklistStore connectivityService?: IConnectivityServiceImplementation fileService?: IFileServiceImplementation offlineService?: IOfflineServiceImplementation gisService?: IGisServiceImplementation config: ClientConfig | BackendConfig models: ApplicationModels constructor(api: Api, config: ClientConfig | BackendConfig) { this.api = api this.config = config this.log = this.initializeLogger() // init services this.auth = new Auth(api) this.elements = new Elements(api) this.batch = new Batch(api) this.listing = new Listing(api) this.exports = new Exports(api) this.organizations = new Organizations(api) this.geojson = new Geojson(api) this.editors = new Editors(api) this.notifications = new Notifications(api) this.tickets = new Tickets(api) this.translations = new Translations(api) this.graph = new Graph(api) this.iot = new Iot(api) this.comments = new Comments(api) this.history = new History(api) this.calendar = new Calendar(api) this.workRecords = new WorkRecords(api) this.registration = new Registration(api) this.privileges = new Privileges(api) this.files = new Files(api) // IMI this.imi = new IMI(api) this.michalovce = new Michalovce(api) this.initializePlatformServices() // init stores this.translationStore = new TranslationStore(this) this.userStore = new UserStore(this) this.elementStore = new ElementStore(this) this.listingStore = new ListingStore(this) this.gisStore = new GisStore(this) this.offlineStore = new OfflineStore(this) this.notificationStore = new NotificationStore(this) this.checklistStore = new ChecklistStore(this) this.models = this.getModels() // Placed at the end of the constructor because `config` and `models` are // required (non-optional) fields with no class-field initializer; with our // `useDefineForClassFields: false` tsconfig, those properties only come // into existence on the instance once assigned above. MobX 6's // `makeObservable` would otherwise throw "Field not found". makeObservable(this, { // `config` and `models` are reference-replaced wholesale (see // `setConfig` / `getModels`), never mutated in place. They must be // `observable.ref` — `observable.deep` would recurse into `models` // and turn the class constructors stored in `elementModels` into // observable proxies, which makes their static `.TYPE` getters return // undefined and breaks element-type → class lookup in `modelMap`. config: observable.ref, models: observable.ref, modelMap: computed, }) } get modelMap(): Map { return new Map( Object.values(this.models?.elementModels || {}) .map((model) => (model ? [model.TYPE, model] : undefined)) .filter(Boolean) as [string, typeof Element][], ) } changeUid(uid: string) { if (this.config && 'user' in this.config) { this.api.changeUid(uid) } else { log.warn('Running in client-mode without UID header') } } restoreUid() { if (this.config && 'user' in this.config) { this.api.changeUid(this.config.user) } else { log.warn('Running in client-mode without UID header') } } initializeLogger() { log.setDefaultLevel('WARN') if (this.config?.logLevel) { log.info(`Setting log level to ${this.config.logLevel}`) log.setLevel(this.config.logLevel) } return log } async initializePlatformServices() { if (!this.fileService) { // Files const fileService = new FileServiceProvider( this.api.apisauce as unknown as ApisauceInstance, ) await fileService.initialize() if (fileService.implementation) { this.fileService = fileService.implementation } } if (!this.offlineService) { // Offline const offlineService = new OfflineServiceProvider( this.api.apisauce as unknown as ApisauceInstance, ) await offlineService.initialize() if (offlineService.implementation) { this.offlineService = offlineService.implementation } } if (!this.connectivityService) { // Connectivity const connectivityService = new ConnectivityServiceProvider( this.api.apisauce as unknown as ApisauceInstance, ) await connectivityService.initialize() if (connectivityService.implementation) { this.connectivityService = connectivityService.implementation } } } initializeUserDependentStores() { this.gisStore = new GisStore(this) this.offlineStore = new OfflineStore(this) this.notificationStore = new NotificationStore(this) } setModels(organization: string) { this.models = this.getModels(organization) } getModels(organization?: string): ApplicationModels { const customModels = (organization && this.config?.organizations?.[organization]?.models) || {} return _.merge( DEFAULT_CONFIG.models, this.config?.default?.models, customModels, ) } }