{"version":3,"file":"app-7be190fb.47e839c4edc29ae0ca22.bundle.js","mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAuNA;AACA;AA4CA;AACA;AApQA;AAEA;AAAA;AAMA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAIA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AAKA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAGA;AAKA;AAEA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;;;AA/EA;AAAA;AAgFA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAIA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAGA;AAKA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAEA;AAAA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AACA;AAGA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAKA;AACA;AAKA;AAAA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAIA;AAEA;AAAA;AAAA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AAKA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AAAA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAhVA;AADA;AAIA;AACA;AACA;AACA;AACA;AACA;AARA;AAiVA;AAAA;AAjVA;AAmVA;AAAA;AA+BA;AA7BA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC3XA;AACA;AAGA;AA2BA;AAAA;AAAA;AA3BA;AAIA;AAAA;;;AAAA;AAGA;AAAA;;;AAAA;AAGA;AAAA;;;AAAA;AAGA;AAAA;;;AAAA;AAGA;AAAA;;;AAAA;AAGA;AAAA;;;AAAA;AAGA;AAAA;;;AAAA;AAGA;AAAA;;;AAAA;AAOA;;;;;AACA;AACA;;AAAA;;;;;;AAEA;AAMA;;;;;AACA;;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;AACA;;AAvDA;AAEA;AAGA;AASA;AAGA;AAGA;AAGA;AAxBA;AADA;AA4BA;AA3BA;AAyDA;AAAA;AAzDA;AA2DA;AAAA;AACA;AACA;AACA;AAAA;AAAA","sources":["webpack://client-app/./src/updateService.ts","webpack://client-app/./src/user.ts"],"sourcesContent":["import { autoinject, BindingEngine } from \"aurelia-framework\";\r\nimport { UpdateClaimApplicationClient, UpdateApplicationRequestInput } from \"./application/gql/updateClaimApplication.tsgql\";\r\nimport { CreateCollectionEntityClient, CreateCollectionEntityRequestInput } from \"./application/gql/CreateCollectionEntity.tsgql\";\r\nimport { RemoveCollectionEntityClient, RemoveCollectionEntityRequestInput } from \"./application/gql/RemoveCollectionEntity.tsgql\";\r\nimport { CountryCodeType } from \"./application/gql/createUpdateApplication.tsgql\";\r\nimport { UpdateClaimApplicationBatchClient, UpdateClaimApplicationBatchRequestInput, UpdateInput } from \"./application/gql/updateClaimApplicationBatch.tsgql\";\r\nimport { EventAggregator } from \"aurelia-event-aggregator\";\r\n\r\n@autoinject()\r\nexport class UpdateService {\r\n\r\n constructor(\r\n private bindingEngine: BindingEngine,\r\n private updateClaimApplication: UpdateClaimApplicationClient,\r\n private createCollectionEntity: CreateCollectionEntityClient,\r\n private removeCollectionEntity: RemoveCollectionEntityClient,\r\n private updateClaimApplicationBatch: UpdateClaimApplicationBatchClient,\r\n private aggregator: EventAggregator)\r\n { }\r\n\r\n setup(\r\n ownerId: string,\r\n applicationId: string,\r\n objectPrefix: string,\r\n object: any) {\r\n //let array = Object.getOwnPropertyNames(object);\r\n let array = Object.getOwnPropertyNames(Object.getPrototypeOf(object).__metadata__);\r\n\r\n const subs = [];\r\n\r\n const prototype = Object.getPrototypeOf(object);\r\n let ignores = prototype.__ignores;\r\n if (!ignores) {\r\n ignores = [];\r\n }\r\n\r\n let updateMembers = prototype.__updateMembers;\r\n if (!updateMembers) {\r\n updateMembers = [];\r\n }\r\n\r\n let updateCollections = prototype.__updateCollections;\r\n if (!updateCollections) {\r\n updateCollections = [];\r\n }\r\n\r\n //console.log('setting up listening to', objectPrefix, object, array);\r\n\r\n for (let i = 0; i < array.length; i++) {\r\n const prop = array[i];\r\n\r\n if (ignores.indexOf(prop) > -1) {\r\n continue;\r\n }\r\n else if (updateMembers.indexOf(prop) > -1) {\r\n const innerObj = object[prop];\r\n const innerObjPrefix = objectPrefix + prop + '.';\r\n\r\n if (innerObj) {\r\n this.setup(\r\n ownerId,\r\n applicationId,\r\n innerObjPrefix,\r\n innerObj);\r\n }\r\n }\r\n else if (updateCollections.indexOf(prop) > -1) {\r\n\r\n const fieldName = objectPrefix + prop;\r\n\r\n const collection = object[prop];\r\n for (let i = 0; i < collection.length; i++) {\r\n const collectionItem = collection[i];\r\n\r\n if (!collectionItem.id) {\r\n console.warn('Collection Item has no Id. Autosave will not work on this item', collectionItem);\r\n }\r\n\r\n const collectionItemPrefix = objectPrefix + prop + '[' + collectionItem.id + ']' + '.';\r\n //console.log('listening to collection item', collectionItem, collectionItemPrefix);\r\n\r\n this.setup(\r\n ownerId,\r\n applicationId,\r\n collectionItemPrefix,\r\n collectionItem);\r\n }\r\n\r\n const sub = this.bindingEngine\r\n .collectionObserver(object[prop])\r\n .subscribe((updates) => {\r\n\r\n for (let i = 0; i < updates.length; i++) {\r\n\r\n const update = updates[i];\r\n\r\n for (let i = 0; i < update.addedCount; i++) {\r\n const newValue = object[prop][update.index + i];\r\n\r\n if (newValue) {\r\n this.addCollectionValue(applicationId, ownerId, fieldName, newValue);\r\n }\r\n }\r\n\r\n const removedValues = update.removed;\r\n\r\n if (removedValues) {\r\n //console.log('removing these', removedValues);\r\n for (let i = 0; i < removedValues.length; i++) {\r\n const removedValue = removedValues[i];\r\n this.removeCollectionValue(applicationId, ownerId, fieldName, removedValue);\r\n }\r\n }\r\n }\r\n \r\n });\r\n\r\n subs.push(sub);\r\n }\r\n else {\r\n\r\n const fieldName = objectPrefix + prop;\r\n const sub = this.bindingEngine\r\n .propertyObserver(object, prop)\r\n .subscribe(this.updateFieldFn(applicationId, ownerId, fieldName));\r\n\r\n subs.push(sub);\r\n }\r\n }\r\n }\r\n\r\n addCollectionValue(applicationId, ownerId, fieldName, newValue) {\r\n const req = new CreateCollectionEntityRequestInput();\r\n req.id = applicationId;\r\n req.ownerId = ownerId;\r\n req.fieldName = fieldName;\r\n req.jsonEntity = JSON.stringify(newValue);\r\n\r\n //console.log('creating entity', req);\r\n\r\n const context = this;\r\n\r\n this.createCollectionEntity.createCollectionEntity_createCollectionEntity(req)\r\n .then(function (result) {\r\n if (result.createCollectionEntity.success) {\r\n newValue.id = result.createCollectionEntity.id;\r\n\r\n if (!newValue.id) {\r\n console.warn('Collection Item has no Id. Autosave will not work on this item', newValue.id);\r\n }\r\n\r\n //listen to the new item\r\n context.setup(\r\n ownerId,\r\n applicationId,\r\n req.fieldName + '[' + newValue.id + ']' + '.',\r\n newValue);\r\n }\r\n else if (result.createCollectionEntity.isLocked) {\r\n context.aggregator.publish('entity-locked');\r\n } else {\r\n throw new Error('Failed to add collection value');\r\n }\r\n });\r\n }\r\n\r\n removeCollectionValue(applicationId, ownerId, fieldName, removedValue) {\r\n const req = new RemoveCollectionEntityRequestInput();\r\n req.id = applicationId;\r\n req.ownerId = ownerId;\r\n req.fieldName = fieldName;\r\n req.collectionEntityId = removedValue.id;\r\n\r\n this.removeCollectionEntity.removeCollectionEntity_removeCollectionEntity(req)\r\n .then((res) => {\r\n if (res.removeCollectionEntity.success) {\r\n this.removedEntities.push(req.fieldName + '[' + req.collectionEntityId + ']');\r\n }\r\n else if (res.removeCollectionEntity.isLocked) {\r\n this.aggregator.publish('entity-locked');\r\n }\r\n else {\r\n throw new Error('Failed to remove collection value');\r\n }\r\n });\r\n }\r\n\r\n updateFieldFn(applicationId, ownerId, fieldName: string) {\r\n return (newValue, oldValue) => {\r\n\r\n // TODO: revisit?\r\n if (fieldName.endsWith(\"Other\")) {\r\n fieldName = fieldName.substr(0, fieldName.length - 5);\r\n }\r\n\r\n const req = new UpdateInput();\r\n req.fieldName = fieldName;\r\n\r\n const valType = typeof newValue;\r\n\r\n if (!!CountryCodeType[newValue]) {\r\n req.fieldValueCountryCodeType = newValue;\r\n }\r\n else if (valType === 'string') {\r\n req.fieldValueString = newValue;\r\n }\r\n else if (valType === 'boolean') {\r\n req.fieldValueBool = newValue\r\n }\r\n else if (newValue instanceof Date) {\r\n req.fieldValueDatetime = newValue;\r\n }\r\n else if (Array.isArray(newValue)) {\r\n req.fieldValueStringList = newValue;\r\n }\r\n \r\n else {\r\n console.warn('unknown valType', valType, newValue);\r\n }\r\n\r\n this.debounce(\r\n () => {\r\n console.log('pushing a save', req, fieldName);\r\n this.pushUpdate(applicationId, ownerId, req);\r\n },\r\n 2000,\r\n false,\r\n applicationId + '|' + ownerId + '|' + req.fieldName\r\n )();\r\n };\r\n }\r\n\r\n removedEntities : string[] = [];\r\n pendingUpdates: UpdateInput[] = [];\r\n\r\n pushUpdate(id: string, ownerId :string, request: UpdateApplicationRequestInput) {\r\n this.pendingUpdates.push(request);\r\n\r\n this.debounce(\r\n () => {\r\n var batchRequest = new UpdateClaimApplicationBatchRequestInput();\r\n batchRequest.id = id;\r\n batchRequest.ownerId = ownerId;\r\n batchRequest.updates = this.pendingUpdates.filter(x => {\r\n // don't attempt to update an entity that we've already removed\r\n return this.removedEntities.filter(y => x.fieldName.indexOf(y) > -1).length === 0;\r\n });\r\n\r\n this.sendBatchUpdateRequest(batchRequest);\r\n\r\n this.pendingUpdates = [];\r\n },\r\n 2500,\r\n false,\r\n id + '|' + ownerId)();\r\n }\r\n\r\n sendBatchUpdateRequest(batchRequest: UpdateClaimApplicationBatchRequestInput, failureCount: number = 0) {\r\n this.inProgress[batchRequest.id] = true;\r\n this.updateClaimApplicationBatch.updateClaimApplicationBatch_updateClaimApplicationBatch(\r\n batchRequest).then(\r\n (result) => {\r\n if (result.updateClaimApplicationBatch.isLocked) {\r\n this.aggregator.publish('entity-locked');\r\n }\r\n if (!result.updateClaimApplicationBatch.success) {\r\n this.handleBatchRequestFailure(batchRequest, failureCount);\r\n }\r\n this.inProgress[batchRequest.id] = null;\r\n console.log('result', result);\r\n },\r\n (errorResult) =>\r\n {\r\n this.handleBatchRequestFailure(batchRequest, failureCount);\r\n });\r\n }\r\n\r\n timeouts = {};\r\n inProgress = {};\r\n\r\n handleBatchRequestFailure(batchRequest: UpdateClaimApplicationBatchRequestInput, failureCount: number) {\r\n if (failureCount < 5) {\r\n failureCount++;\r\n const timeout = (2 ** failureCount) * 50;\r\n console.warn('Sending batch updates has failed. Retry attempt ' + failureCount + ' in ' + timeout + 'ms');\r\n setTimeout(() => {\r\n this.sendBatchUpdateRequest(batchRequest, failureCount);\r\n }, timeout);\r\n }\r\n else {\r\n console.error('Failed to push update ' + failureCount + ' times. No retries remaining');\r\n this.aggregator.publish('save-error');\r\n }\r\n }\r\n\r\n /* \r\n * Based from https://davidwalsh.name/javascript-debounce-function \r\n * but keeps a collection of timers based on the key parameter\r\n */\r\n debounce(func, wait, immediate, key) {\r\n var context = this;\r\n return function () {\r\n var args = arguments;\r\n var later = function () {\r\n context.timeouts[key] = null;\r\n if (!immediate) func.apply(context, args);\r\n };\r\n var callNow = immediate && !context.timeouts[key];\r\n clearTimeout(context.timeouts[key]);\r\n context.timeouts[key] = setTimeout(later, wait);\r\n if (callNow) func.apply(context, args);\r\n };\r\n };\r\n\r\n updatesPending(): boolean {\r\n const timers = Object\r\n .entries(this.timeouts)\r\n .filter((k) => k[1] !== null && k[1] !== undefined);\r\n\r\n const inPro = Object\r\n .entries(this.inProgress)\r\n .filter((k) => k[1] !== null && k[1] !== undefined);\r\n\r\n return timers.length > 0 || inPro.length > 0;\r\n }\r\n\r\n awaitUpdates(): Promise {\r\n var context = this;\r\n\r\n if (!context.updatesPending()) {\r\n return new Promise((resolve) => { resolve(true); })\r\n }\r\n else {\r\n return new Promise((resolve, reject) => {\r\n let wait: any;\r\n var later = function () {\r\n const updatesPending = context.updatesPending();\r\n if (!updatesPending) {\r\n clearTimeout(wait);\r\n resolve(true)\r\n }\r\n }\r\n wait = setInterval(later, 500);\r\n });\r\n }\r\n }\r\n}\r\n\r\nexport class Update {\r\n\r\n static UpdateNested(): any {\r\n return function (target: any, propertyKey: string) {\r\n if (!target.__updateMembers) {\r\n target.__updateMembers = []\r\n }\r\n\r\n target.__updateMembers.push(propertyKey);\r\n }\r\n }\r\n\r\n static UpdateNestedCollection(): any {\r\n return function (target: any, propertyKey: string) {\r\n if (!target.__updateCollections) {\r\n target.__updateCollections = []\r\n }\r\n\r\n target.__updateCollections.push(propertyKey);\r\n }\r\n }\r\n\r\n static IgnoreUpdates(): any {\r\n return function (target: any, propertyKey: string) {\r\n if (!target.__ignores) {\r\n target.__ignores = []\r\n }\r\n\r\n target.__ignores.push(propertyKey);\r\n }\r\n }\r\n}\r\n","import { autoinject } from \"aurelia-framework\";\r\nimport { UserDetailsClient } from \"./application/gql/userDetails.tsgql\";\r\n\r\n@autoinject\r\nexport class CurrentUser {\r\n private static _loaded: boolean = false;\r\n\r\n private static _isLoggedIn: boolean = false;\r\n public get isLoggedIn() { return CurrentUser._isLoggedIn; }\r\n\r\n private static _name: string = \"name\";\r\n public get name() { return CurrentUser._name; }\r\n\r\n private static _email: string;\r\n public get email() { return CurrentUser._email; }\r\n\r\n private static _id: string;\r\n public get id() { return CurrentUser._id; }\r\n\r\n private static _isAdmin: boolean = false;\r\n public get isAdmin() { return CurrentUser._isAdmin; }\r\n\r\n private static _isFullyAuthenticated: boolean = false;\r\n public get isFullyAuthenticated() { return CurrentUser._isFullyAuthenticated; }\r\n\r\n private static _applicationIds: string[] = [];\r\n public get applicationIds() { return CurrentUser._applicationIds; }\r\n\r\n private static _expired: boolean = false;\r\n public get expired() { return CurrentUser._expired; }\r\n\r\n constructor(private userDetailsClient: UserDetailsClient) { }\r\n\r\n /** Call this before attempting to get user details.\r\n * If you think data may have changed call ReloadUser()\r\n * */\r\n public async LoadUser() {\r\n if (!CurrentUser._loaded) {\r\n await this.ReloadUser();\r\n }\r\n }\r\n\r\n /** Call this if you think user details may have changed\r\n * \r\n This forces a call to the api \r\n */\r\n public async ReloadUser() {\r\n var result = await this.userDetailsClient.userDetails_userDetails();\r\n\r\n if (result.me) {\r\n CurrentUser._loaded = true;\r\n CurrentUser._isLoggedIn = result.me.isAuthenticated;\r\n CurrentUser._name = result.me.name;\r\n CurrentUser._email = result.me.emailAddress;\r\n CurrentUser._id = result.me.id;\r\n CurrentUser._isAdmin = result.me.isAdmin;\r\n CurrentUser._isFullyAuthenticated = result.me.isFullyAuthenticated;\r\n CurrentUser._applicationIds = result.me.applicationIds;\r\n CurrentUser._expired = result.me.expired;\r\n }\r\n }\r\n}\r\n\r\nexport class Account {\r\n public name: string = \"\";\r\n public accountId: string = \"\";\r\n}\r\n"],"names":[],"sourceRoot":""}