Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/store/flow/flow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export const getters: GetterTree<IFlowsState, IRootState> = {
}
}
},
blockUuidsOnActiveFlow: (state, getters): IBlock['uuid'][] => getters.activeFlow?.blocks,
blocksOnActiveFlow: (state, getters): IBlock[] => getters.activeFlow?.blocks,
isActiveFlowValid: (state, getters, rootState) => {
const flowValidationResult = get(rootState.validation.validationStatuses, `flow/${getters.activeFlow.uuid}`)
if (flowValidationResult && !flowValidationResult.isValid) {
Expand Down
2 changes: 2 additions & 0 deletions src/store/flow/resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ export const getters: GetterTree<IFlowsState, IRootState> = {
resourcesByUuidOnActiveFlow: (_state, getters) => keyBy(getters.activeFlow.resources, 'uuid'),

resourceUuidsOnActiveFlow: (_state, getters) => compact(map(getters.activeFlow.resources, (res) => res.uuid)),

resourcesOnActiveFlow: (_state, getters) => getters.activeFlow.resources,
}

export const mutations: MutationTree<IFlowsState> = {
Expand Down
50 changes: 46 additions & 4 deletions src/store/validation/index.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
import Vue from 'vue'
import {ActionTree, GetterTree, Module, MutationTree} from 'vuex'
import {ActionTree, GetterTree, Module, MutationTree, Store} from 'vuex'
import {IRootState} from '@/store'
import {ErrorObject} from 'ajv'
import {
IBlock,
IContainer,
IFlow,
ILanguage,
getFlowStructureErrors,
IResource,
SupportedContentType,
SupportedMode,

} from '@floip/flow-runner'
import {cloneDeep, each, filter, get, forIn, includes, intersection, isEmpty, map, uniqBy} from 'lodash'
import {cloneDeep, each, filter, get, forIn, includes, intersection, isEmpty, map, uniqBy, debounce} from 'lodash'
import {
debugValidationStatus,
flatValidationStatuses,
Expand Down Expand Up @@ -298,6 +296,50 @@ export const actions: ActionTree<IValidationState, IRootState> = {
},
}

export function registerWatchers(store: Store<IRootState>): void {
const DEBOUNCE_VALIDATION_TIMER_MS = 300

store.watch(
(_, getters) => getters['flow/activeFlow'],
newFlow => debounce(
async () => {
console.debug('watch/activeFlow:', 'active flow has changed', 'Validating ...')
await store.dispatch('validate_flow', {flow: newFlow})
},
DEBOUNCE_VALIDATION_TIMER_MS,
),
{deep: true, immediate: true},
)

store.watch(
(_, getters) => getters['flow/blocksOnActiveFlow'],
() => debounce(
async () => {
console.debug('watch/activeFlow.blocks:', 'blocks inside active flow have changed', 'Validating ...')
await store.dispatch('validate_allBlocksWithinFlow')
},
DEBOUNCE_VALIDATION_TIMER_MS,
),
{deep: true, immediate: true},
)

store.watch(
(_, getters) => getters['flow/resourcesOnActiveFlow'],
newResources => debounce(
async () => {
console.debug('watch/activeFlow.resources:', 'resources inside active flow have changed', 'Validating ...')
await store.dispatch('validate_resourcesOnSupportedValues', {
resources: newResources,
supportedModes: this.activeFlow.supported_modes,
supportedLanguages: this.activeFlow.languages,
})
},
DEBOUNCE_VALIDATION_TIMER_MS,
),
{deep: true, immediate: true},
)
}

export const store: Module<IValidationState, IRootState> = {
namespaced: true,
state: stateFactory,
Expand Down
56 changes: 8 additions & 48 deletions src/views/InteractionDesigner.vue
Original file line number Diff line number Diff line change
Expand Up @@ -48,20 +48,20 @@
</template>

<script lang="ts">
import {cloneDeep, debounce, endsWith, forEach, get, includes, invoke, isEmpty, values} from 'lodash'
import {endsWith, forEach, get, includes, invoke, isEmpty, values} from 'lodash'
import {mixins} from 'vue-class-component'
import {Component, Prop, Vue, Watch} from 'vue-property-decorator'
import {Action, Getter, Mutation, namespace, State} from 'vuex-class'
import Lang from '@/lib/filters/lang'
import Routes from '@/lib/mixins/Routes'
import {scrollBehavior, scrollBlockIntoView} from '@/router/helpers'
import {store} from '@/store'
import {IRootState, store} from '@/store'
import ClipboardRoot from '@/components/interaction-designer/clipboard/ClipboardRoot.vue'
import {Route} from 'vue-router'
import {IBlock, IFlow, ILanguage, IResource, IResources, SupportedMode} from '@floip/flow-runner'
import {IBlock, IFlow} from '@floip/flow-runner'
import {ErrorObject} from 'ajv'
import {MutationPayload} from 'vuex'
import {IValidationStatus} from '@/store/validation'
import {MutationPayload, Store} from 'vuex'
import {registerWatchers} from '@/store/validation'

Component.registerHooks(['beforeRouteUpdate'])

Expand All @@ -70,8 +70,6 @@ const builderNamespace = namespace('builder')
const clipboardNamespace = namespace('clipboard')
const validationVuexNamespace = namespace('validation')

const DEBOUNCE_VALIDATION_TIMER_MS = 300

@Component({
components: {
ClipboardRoot,
Expand All @@ -95,26 +93,6 @@ export class InteractionDesigner extends mixins(Lang, Routes) {
},
}) readonly builderConfig!: object

@validationVuexNamespace.Action validate_flow!: ({flow}: { flow: IFlow }) => Promise<IValidationStatus>
debounceFlowValidation = debounce(function (this: any, {newFlow}: {newFlow: IFlow}) {
console.debug('watch/activeFlow:', 'active flow has changed', `from ${this.mainComponent}.`, 'Validating ...')
this.validate_flow({flow: newFlow})
}, DEBOUNCE_VALIDATION_TIMER_MS)
@validationVuexNamespace.Action validate_allBlocksWithinFlow!: () => Promise<void>
debounceBlockValidation = debounce(function (this: any) {
console.debug('watch/activeFlow.blocks:', 'blocks inside active flow have changed', `from ${this.mainComponent}.`, 'Validating ...')
this.validate_allBlocksWithinFlow()
}, DEBOUNCE_VALIDATION_TIMER_MS)
@validationVuexNamespace.Action validate_resourcesOnSupportedValues!: (
{resources, supportedModes, supportedLanguages}: {resources: IResource[], supportedModes: SupportedMode[], supportedLanguages: ILanguage[]}
) => Promise<void>

get blocksOnActiveFlowForWatcher(): IBlock[] {
// needed to make comparison between new & old values on watcher
return cloneDeep(this.activeFlow.blocks)
}
// ] ######### end Validation API Watchers

toolbarHeight = 102
// TODO: Move this to BlockClassDetails spec // an inversion can be "legacy types"
pureVuejsBlocks = [
Expand Down Expand Up @@ -170,27 +148,6 @@ export class InteractionDesigner extends mixins(Lang, Routes) {
@builderNamespace.Getter isResourceViewerCanvasEnabled!: boolean
@clipboardNamespace.Getter isSimulatorActive!: boolean

// ###### Validation API Watchers [
@Watch('activeFlow', {deep: true, immediate: true})
async onActiveFlowChanged(newFlow: IFlow): Promise<void> {
this.debounceFlowValidation({newFlow})
}

@Watch('blocksOnActiveFlowForWatcher', {deep: true, immediate: true})
async onBlocksInActiveFlowChanged(newBlocks: IBlock[], oldBlocks: IBlock[]): Promise<void> {
this.debounceBlockValidation()
}

@Watch('activeFlow.resources', {deep: true, immediate: true})
async onResourcesOnActiveFlowChanged(newResources: IResources, oldResources: IResources): Promise<void> {
console.debug('watch/activeFlow.resources:', 'resources inside active flow have changed', `from ${this.mainComponent}.`, 'Validating ...')
await this.validate_resourcesOnSupportedValues({
resources: newResources,
supportedModes: this.activeFlow.supported_modes,
supportedLanguages: this.activeFlow.languages,
})
}

get jsKey(): string {
return get(this.selectedBlock, 'jsKey')
}
Expand Down Expand Up @@ -230,6 +187,9 @@ export class InteractionDesigner extends mixins(Lang, Routes) {
// Listen to Flow updates to maintain "hasFlowChanges" state
this.$store.subscribe(this.handleFlowChanges.bind(this))

// watch state and run validations
registerWatchers(this.$store as Store<IRootState>)

this.initializeTreeModel()
// `this.mode` comes from captured param in js-routes
this.updateIsEditableFromParams(this.mode)
Expand Down