/* * CloudBeaver - Cloud Database Manager * Copyright (C) 2020-2024 DBeaver Corp and others * * Licensed under the Apache License, Version 2.0. * you may not use this file except in compliance with the License. */ import { action, makeObservable, observable } from 'mobx'; import { type ISyncExecutor, SyncExecutor } from '@cloudbeaver/core-executor'; import { isNotNullDefined } from '@cloudbeaver/core-utils'; import type { ISettingChangeData, ISettingsSource } from './ISettingsSource.js'; export abstract class SettingsSource implements ISettingsSource { readonly onChange: ISyncExecutor; private updating: boolean; protected readonly changes: Map; constructor() { this.onChange = new SyncExecutor(); this.updating = false; this.changes = new Map(); makeObservable(this, { changes: observable.shallow, update: action, }); } has(key: any): boolean { return this.changes.has(key); } isEdited(key?: any): boolean { if (isNotNullDefined(key)) { return this.changes.has(key); } return this.changes.size > 0; } protected abstract getSnapshot(): Record; abstract isReadOnly(key: any): boolean; abstract getValue(key: any): any; abstract save(): Promise; clear(): void { this.changes.clear(); } getEditedValue(key: any): any { if (this.changes.has(key)) { return this.changes.get(key); } return this.getValue(key); } setValue(key: any, value: any): void { const currentValue = this.getValue(key); if (currentValue === value || (!isNotNullDefined(currentValue) && value === null)) { this.changes.delete(key); } else { this.changes.set(key, value); } } protected update(action: () => void) { if (this.updating) { action(); return; } this.updating = true; try { const snapshot = this.getSnapshot(); action(); const newSnapshot = this.getSnapshot(); for (const [key, value] of Object.entries(newSnapshot)) { if (snapshot[key] !== value) { this.onChange.execute({ key, value }); } } for (const key of Object.keys(snapshot)) { if (!(key in newSnapshot)) { this.onChange.execute({ key, value: undefined }); } } } finally { this.updating = false; } } }