cv-dialog-sdk
Version:
Catavolt Dialog Javascript API
354 lines (353 loc) • 13.9 kB
JavaScript
import { DataUrl } from '../util/DataUrl';
import { Attachment } from './Attachment';
import { Details } from "./Details";
import { Dialog } from './Dialog';
import { LargeProperty } from './LargeProperty';
import { NullRecord } from './NullRecord';
import { Property } from './Property';
import { RecordBuffer } from './RecordBuffer';
import { RecordUtil } from './RecordUtil';
import { RedirectionUtil } from './RedirectionUtil';
import { TypeNames } from './types';
import { ViewModeEnum } from './types';
/**
* PanContext Subtype that represents an 'Editor Dialog'.
* An 'Editor' represents and is backed by a single Record and Record definition.
* See {@link Record} and {@link RecordDef}.
*/
export class EditorDialog extends Dialog {
static getSubType(jsonObj) {
if (Dialog.isSearchDialog(jsonObj)) {
return 'SearchDialog';
}
if (jsonObj.view && jsonObj.view.type === TypeNames.FormTypeName) {
return 'FormDialog';
}
return 'EditorDialog';
}
changeViewMode(viewMode) {
if (this.viewMode !== viewMode) {
return this.catavolt.dialogApi
.changeMode(this.tenantId, this.sessionId, this.id, viewMode)
.then((result) => {
if (RedirectionUtil.isRedirection(result)) {
this.updateSettingsWithNewDialogProperties(result.referringObject);
if (result.refreshNeeded) {
this.catavolt.dataLastChangedTime = new Date();
}
return result;
}
else {
const dialog = result;
// any new dialog needs to be initialized with the Catavolt object
dialog.initialize(this.catavolt);
this.updateSettingsWithNewDialogProperties(dialog.referringObject);
return dialog;
}
});
}
}
/**
* Get the associated properties
*/
get props() {
return this.record.properties;
}
get propertyDefs() {
return this.recordDef.propertyDefs;
}
get constants() {
if (this.view instanceof Details) {
return this.view.constants;
}
return null;
}
get attributeCells() {
if (this.view instanceof Details) {
return this.view.attributeCells;
}
return null;
}
get labelsByPropName() {
if (this.view instanceof Details) {
return this.view.labelsByPropName;
}
return null;
}
/**
* Get the associated entity record
* @returns {Record}
*/
get record() {
return this.buffer.toRecord();
}
/**
* Get the current version of the entity record, with any pending changes present
* @returns {Record}
*/
get recordNow() {
return this.record;
}
getAvailableValues(propName) {
return this.catavolt.dialogApi.getAvailableValues(this.tenantId, this.sessionId, this.id, propName, {
pendingWrites: this.getWriteableRecord(this.buffer.afterEffects()),
type: TypeNames.AvailableValuesParametersTypeName,
});
}
/**
* Returns whether or not this cell definition contains a binary value
*
* @param {AttributeCellValue} cellValue
* @returns {boolean}
*/
isBinary(cellValue) {
const propDef = this.propDefAtName(cellValue.propertyName);
return propDef && (propDef.isLargePropertyType || (propDef.isURLType && cellValue.isInlineMediaStyle));
}
/**
* Returns whether or not this Editor is in 'read' mode
* @returns {boolean}
*/
get isReadMode() {
return this.viewMode === ViewModeEnum.READ;
}
/**
* Returns whether or not this property is read-only
* @param propName
* @returns {boolean}
*/
isReadModeFor(propName) {
if (!this.isReadMode) {
const propDef = this.propDefAtName(propName);
return !propDef || propDef.isReadOnly;
}
return true;
}
/**
* Returns whether or not this cell definition contains a binary value that should be treated as a signature control
* @param cellValueDef
* @returns {PropertyDef|boolean}
*/
isSignature(cellValueDef) {
const propDef = this.propDefAtName(cellValueDef.propertyName);
return propDef.isSignatureType;
}
/**
* Returns whether or not this property is 'writable'
* @returns {boolean}
*/
get isWriteMode() {
return this.viewMode === ViewModeEnum.WRITE;
}
performMenuActionWithId(actionId) {
return this.invokeMenuActionWithId(actionId, {
pendingWrites: this.getWriteableRecord(this.buffer.afterEffects()),
type: TypeNames.ActionParametersTypeName
}).then(result => {
return result;
});
}
/**
* Perform the action associated with the given Menu on this EditorDialog
* Given that the Editor could possibly be destroyed as a result of this action,
* any provided pending writes will be saved if present.
* @param {Menu} menu
* @param {Record} pendingWrites
* @returns {Promise<{actionId: string} | Redirection>}
*/
performMenuAction(menu) {
return this.invokeMenuAction(menu, {
pendingWrites: this.getWriteableRecord(this.buffer.afterEffects()),
type: TypeNames.ActionParametersTypeName
}).then(result => {
return result;
});
}
/**
* Properties whose {@link PropertyDef.canCauseSideEffects} value is true, may change other underlying values in the model.
* This method will update those underlying values, given the property name that is changing, and the new value.
* This is frequently used with {@link EditorDialog.getAvailableValues}. When a value is selected, other properties'
* available values may change. (i.e. Country, State, City dropdowns)
*/
processSideEffects(propertyName, value) {
const property = this.newProperty(propertyName, value);
const sideEffectsParameters = {
property,
pendingWrites: this.getWriteableRecord(this.buffer.afterEffects()),
type: TypeNames.SideEffectsParameters
};
return this.catavolt.dialogApi
.propertyChange(this.tenantId, this.sessionId, this.id, propertyName, sideEffectsParameters)
.then((sideEffectsResponse) => {
/* TODO */
// coorindate the the handling of this result with server-side (some of these may be null...)
if (sideEffectsResponse.recordDef) {
this.recordDef = sideEffectsResponse.recordDef;
}
const sideEffectsRecord = sideEffectsResponse.record;
const originalProperties = this.buffer.before.properties;
const userModifiedProperties = this.buffer.afterEffects().properties;
const sideEffectsProperties = sideEffectsRecord ? sideEffectsRecord.properties.filter((prop) => {
return prop.name !== propertyName;
}) : [];
this._buffer = RecordBuffer.createRecordBuffer(this.buffer.id, RecordUtil.unionRight(originalProperties, sideEffectsProperties), RecordUtil.unionRight(originalProperties, RecordUtil.unionRight(userModifiedProperties, sideEffectsProperties)), this.record.annotations);
return Promise.resolve();
});
}
/**
* Read (load) the {@link Record} assocated with this Editor
* The record must be read at least once to initialize the Context
* @returns {Future<Record>}
*/
read() {
return this.catavolt.dialogApi.getRecord(this.tenantId, this.sessionId, this.id).then((record) => {
this.initBuffer(record);
this.lastRefreshTime = new Date();
return this.record;
});
}
/**
* Set the value of a property in this {@link Record}.
* Values may be already constructed target types (CodeRef, TimeValue, Date, etc.)
* or primitives, in which case the values will be parsed and objects constructed as necessary.
* @param name
* @param value
* @returns {any}
*/
setPropertyValue(name, value) {
const propDef = this.propDefAtName(name);
let property = null;
if (propDef) {
const parsedValue = value !== null && value !== undefined ? this.parseValue(value, propDef.propertyName) : null;
property = this.buffer.setValue(propDef.propertyName, parsedValue, propDef);
}
return property;
}
newProperty(name, value) {
const propDef = this.propDefAtName(name);
const property = this.buffer.propAtName(name);
let newProperty = null;
if (propDef) {
const parsedValue = value !== null && value !== undefined ? this.parseValue(value, propDef.propertyName) : null;
newProperty = new Property(property.name, parsedValue, propDef.format, property.annotations);
}
return newProperty;
}
newLargePropertyWithDataUrl(dataUrl) {
if (dataUrl) {
const urlObj = new DataUrl(dataUrl);
return this.newLargePropertyWithEncodedData(urlObj.data, urlObj.mimeType);
}
return null;
}
newLargePropertyWithEncodedData(encodedData, mimeType) {
return new LargeProperty(encodedData, mimeType);
}
/**
* Set a binary property from a string formatted as a 'data url'
* See {@link https://en.wikipedia.org/wiki/Data_URI_scheme}
* @param name
* @param dataUrl
*/
setLargePropertyWithDataUrl(name, dataUrl) {
if (dataUrl) {
const urlObj = new DataUrl(dataUrl);
return this.setLargePropertyWithEncodedData(name, urlObj.data, urlObj.mimeType);
}
else {
return this.setPropertyValue(name, null); // Property is being deleted/cleared
}
}
/**
* Set a binary property with base64 encoded data
* @param name
* @param encodedData
* @param mimeType
*/
setLargePropertyWithEncodedData(name, encodedData, mimeType) {
const propDef = this.propDefAtName(name);
let property = null;
if (propDef) {
const value = new LargeProperty(encodedData, mimeType);
property = this.buffer.setValue(propDef.propertyName, value, propDef);
}
return property;
}
/**
* Write this record (i.e. {@link Record}} back to the server
* @returns {Promise<Record | Redirection>}
*/
write() {
const deltaRec = this.buffer.afterEffects();
/* Write the 'special' props first */
return this.writeLargeProperties(deltaRec).then((binResult) => {
return this.writeAttachments(deltaRec).then((atResult) => {
/* Remove special property types before writing the actual record */
const writableRecord = this.getWriteableRecord(deltaRec);
return this.catavolt.dialogApi
.putRecord(this.tenantId, this.sessionId, this.id, writableRecord)
.then((result) => {
const now = new Date();
this.catavolt.dataLastChangedTime = now;
this.lastRefreshTime = now;
if (RedirectionUtil.isRedirection(result)) {
this.updateSettingsWithNewDialogProperties(result.referringObject);
if (result.refreshNeeded) {
this.catavolt.dataLastChangedTime = new Date();
}
return result;
}
else {
const dialog = result;
// any new dialog needs to be initialized with the Catavolt object
dialog.initialize(this.catavolt);
this.updateSettingsWithNewDialogProperties(dialog.referringObject);
return dialog;
}
});
});
});
}
// protected methods
getProperty(params, propertyName) {
return this.catavolt.dialogApi.getEditorProperty(this.tenantId, this.sessionId, this.id, propertyName, params);
}
// Private methods
/**
* Get the current buffered record
* @returns {RecordBuffer}
*/
get buffer() {
if (!this._buffer) {
this._buffer = new RecordBuffer(NullRecord.singleton);
}
return this._buffer;
}
// We have to remove LargeProperties and replace Attachments
// As they are written separately
getWriteableRecord(record) {
const properties = record.properties
.filter((prop) => {
/* Remove the Binary(s) as they have been written separately */
return !this.propDefAtName(prop.name).isLargePropertyType;
})
.map((prop) => {
/*
Remove the Attachment(s) (as they have been written separately) but replace
the property value with the file name of the attachment prior to writing
*/
if (prop.value instanceof Attachment) {
const attachment = prop.value;
const propDef = this.propDefAtName(prop.name);
return new Property(prop.name, attachment.name, propDef.format, prop.annotations);
}
else {
return prop;
}
});
return RecordUtil.newRecord(record.id, properties, record.annotations);
}
initBuffer(record) {
this._buffer = record ? new RecordBuffer(record) : new RecordBuffer(NullRecord.singleton);
}
}