Files
standardnotes-app-web/packages/snjs/lib/Services/Challenge/ChallengeOperation.ts
Karol Sójko b2faa815e9 feat: add sending user requests to process (#1908)
* feat: add sending user requests to process

* fix(snjs): yarn lock

* fix(snjs): imports

* fix: specs
2022-11-02 11:33:02 +01:00

108 lines
3.5 KiB
TypeScript

import { Challenge, ChallengeValue, ChallengeArtifacts } from '@standardnotes/services'
import { ChallengeResponse } from './ChallengeResponse'
import { removeFromArray } from '@standardnotes/utils'
import { ValueCallback } from './ChallengeService'
/**
* A challenge operation stores user-submitted values and callbacks.
* When its values are updated, it will trigger the associated callbacks (valid/invalid/complete)
*/
export class ChallengeOperation {
private nonvalidatedValues: ChallengeValue[] = []
private validValues: ChallengeValue[] = []
private invalidValues: ChallengeValue[] = []
private artifacts: ChallengeArtifacts = {}
constructor(
public challenge: Challenge,
public onValidValue: ValueCallback,
public onInvalidValue: ValueCallback,
public onNonvalidatedSubmit: (response: ChallengeResponse) => void,
public onComplete: (response: ChallengeResponse) => void,
public onCancel: () => void,
) {}
deinit() {
;(this.challenge as unknown) = undefined
;(this.onValidValue as unknown) = undefined
;(this.onInvalidValue as unknown) = undefined
;(this.onNonvalidatedSubmit as unknown) = undefined
;(this.onComplete as unknown) = undefined
;(this.onCancel as unknown) = undefined
;(this.nonvalidatedValues as unknown) = undefined
;(this.validValues as unknown) = undefined
;(this.invalidValues as unknown) = undefined
;(this.artifacts as unknown) = undefined
}
/**
* Mark this challenge as complete, triggering the resolve function,
* as well as notifying the client
*/
public complete(response?: ChallengeResponse) {
if (!response) {
response = new ChallengeResponse(this.challenge, this.validValues, this.artifacts)
}
this.onComplete?.(response)
}
public nonvalidatedSubmit() {
const response = new ChallengeResponse(this.challenge, this.nonvalidatedValues.slice(), this.artifacts)
this.onNonvalidatedSubmit?.(response)
/** Reset values */
this.nonvalidatedValues = []
}
public cancel() {
this.onCancel?.()
}
/**
* @returns Returns true if the challenge has received all valid responses
*/
public isFinished() {
return this.validValues.length === this.challenge.prompts.length
}
private nonvalidatedPrompts() {
return this.challenge.prompts.filter((p) => !p.validates)
}
public addNonvalidatedValue(value: ChallengeValue) {
const valuesArray = this.nonvalidatedValues
const matching = valuesArray.find((v) => v.prompt.id === value.prompt.id)
if (matching) {
removeFromArray(valuesArray, matching)
}
valuesArray.push(value)
if (this.nonvalidatedValues.length === this.nonvalidatedPrompts().length) {
this.nonvalidatedSubmit()
}
}
/**
* Sets the values validation status, as well as handles subsequent actions,
* such as completing the operation if all valid values are supplied, as well as
* notifying the client of this new value's validation status.
*/
public setValueStatus(value: ChallengeValue, valid: boolean, artifacts?: ChallengeArtifacts) {
const valuesArray = valid ? this.validValues : this.invalidValues
const matching = valuesArray.find((v) => v.prompt.validation === value.prompt.validation)
if (matching) {
removeFromArray(valuesArray, matching)
}
valuesArray.push(value)
Object.assign(this.artifacts, artifacts)
if (this.isFinished()) {
this.complete()
} else {
if (valid) {
this.onValidValue?.(value)
} else {
this.onInvalidValue?.(value)
}
}
}
}