fix(nucleus): wait for curr selections (#117)

This commit is contained in:
Miralem Drek
2019-09-26 17:33:15 +02:00
committed by GitHub
parent aad5603e05
commit f9e286d332
9 changed files with 95 additions and 81 deletions

View File

@@ -1,19 +1,6 @@
import vizualizationAPI from '../viz'; import vizualizationAPI from '../viz';
import ObjectAPI from './object-api'; import ObjectAPI from './object-api';
import { observe } from './observer';
export function observe(model, objectAPI) {
const onChanged = () =>
model.getLayout().then(layout => {
objectAPI.setLayout(layout);
});
model.on('changed', onChanged);
model.once('closed', () => {
model.removeListener('changed', onChanged);
objectAPI.close();
});
onChanged();
}
export default function initiate(getCfg, optional, context) { export default function initiate(getCfg, optional, context) {
return context.app.getObject(getCfg.id).then(model => { return context.app.getObject(getCfg.id).then(model => {
@@ -24,7 +11,7 @@ export default function initiate(getCfg, optional, context) {
const objectAPI = new ObjectAPI(model, context, viz); const objectAPI = new ObjectAPI(model, context, viz);
observe(model, objectAPI); observe(model, layout => objectAPI.setLayout(layout)); // TODO - call unobserve when viz is destroyed
const api = objectAPI.getPublicAPI(); const api = objectAPI.getPublicAPI();

View File

@@ -46,14 +46,22 @@ export function observe(model, callback, property = 'layout') {
affected.forEach(key => { affected.forEach(key => {
c.props[key].state = STATES.VALIDATING; c.props[key].state = STATES.VALIDATING;
const method = OBSERVABLE[key].filter(m => model[m])[0]; const method = OBSERVABLE[key].filter(m => model[m])[0];
model[method]().then(value => { model[method]()
.then(value => {
if (cache[model.id] && cache[model.id].props[key]) { if (cache[model.id] && cache[model.id].props[key]) {
if (cache[model.id].props[key].state < STATES.CLOSED && cache[model.id].props[key].state !== STATES.VALID) { if (
cache[model.id].props[key].state < STATES.CLOSED &&
cache[model.id].props[key].state !== STATES.VALID
) {
cache[model.id].props[key].state = STATES.VALID; cache[model.id].props[key].state = STATES.VALID;
cache[model.id].props[key].value = value; cache[model.id].props[key].value = value;
cache[model.id].props[key].callbacks.forEach(cb => cb(value)); cache[model.id].props[key].callbacks.forEach(cb => cb(value));
} }
} }
})
.catch(() => {
// TODO - retry?
cache[model.id].props[key].state = STATES.INVALID;
}); });
}); });
}; };

View File

@@ -15,11 +15,13 @@ const create = app => {
let modalObject; let modalObject;
// let mounted; // let mounted;
let lyt; let lyt;
let currentSelectionsModel;
let prom;
const api = { const api = {
model: app, model: app,
switchModal(object, path, accept = true) { switchModal(object, path, accept = true) {
if (object === modalObject) { if (object === modalObject) {
return Promise.resolve(); return prom || Promise.resolve();
} }
if (modalObject) { if (modalObject) {
modalObject.endSelections(accept); modalObject.endSelections(accept);
@@ -30,17 +32,22 @@ const create = app => {
// TODO check model state // TODO check model state
modalObject = object; modalObject = object;
api.emit('modal', modalObject._selections); api.emit('modal', modalObject._selections);
return modalObject.beginSelections(Array.isArray(path) ? path : [path]).catch(err => { prom = currentSelectionsModel.then(() => {
// do not return the call to beginSelection to avoid waiting for it's response
modalObject.beginSelections(Array.isArray(path) ? path : [path]).catch(err => {
if (err.code === 6003) { if (err.code === 6003) {
// If another object already is in modal -> abort and take over // If another object already is in modal -> abort and take over
return api.abortModal().then(() => object.beginSelections(Array.isArray(path) ? path : [path])); return api.abortModal().then(() => object.beginSelections(Array.isArray(path) ? path : [path]));
} }
throw err; throw err;
}); });
});
return prom;
} }
modalObject = null; modalObject = null;
api.emit('modal-unset'); api.emit('modal-unset');
return Promise.resolve(); prom = Promise.resolve();
return prom;
}, },
isModal(objectModel) { isModal(objectModel) {
// TODO check model state // TODO check model state
@@ -83,7 +90,7 @@ const create = app => {
eventmixin(api); eventmixin(api);
modelCache( currentSelectionsModel = modelCache(
{ {
qInfo: { qInfo: {
qType: 'current-selections', qType: 'current-selections',
@@ -94,7 +101,8 @@ const create = app => {
alternateStates: [], alternateStates: [],
}, },
app app
).then(model => { )
.then(model => {
observe(app, appLayout => { observe(app, appLayout => {
const states = [...appLayout.qStateNames].map(s => ({ const states = [...appLayout.qStateNames].map(s => ({
stateName: s, // need this as reference in selection toolbar since qSelectionObject.qStateName is not in the layout stateName: s, // need this as reference in selection toolbar since qSelectionObject.qStateName is not in the layout
@@ -134,6 +142,9 @@ const create = app => {
app._selections = null; // eslint-disable-line no-param-reassign app._selections = null; // eslint-disable-line no-param-reassign
cache[app.id] = null; cache[app.id] = null;
}); });
})
.catch(() => {
// do something
}); });
return api; return api;

View File

@@ -55,16 +55,18 @@ export default function(model, app) {
return appAPI().switchModal(null, null, false, false); return appAPI().switchModal(null, null, false, false);
}, },
select(s) { select(s) {
this.begin([s.params[0]]); const b = this.begin([s.params[0]]);
if (!appAPI().isModal()) { if (!appAPI().isModal()) {
return; return;
} }
hasSelected = true; hasSelected = true;
b.then(() =>
model[s.method](...s.params).then(qSuccess => { model[s.method](...s.params).then(qSuccess => {
if (!qSuccess) { if (!qSuccess) {
this.clear(); this.clear();
} }
}); })
);
}, },
canClear() { canClear() {
return hasSelected && layout.qSelectionInfo.qMadeSelections; return hasSelected && layout.qSelectionInfo.qMadeSelections;

View File

@@ -18,7 +18,7 @@
"build": "nebula build", "build": "nebula build",
"lint": "eslint src", "lint": "eslint src",
"start": "nebula serve", "start": "nebula serve",
"test:integration": "aw puppet --testExt '*.int.js' --glob 'test/integration/**/*.int.js' --chrome.headless true --mocha.timeout 15000" "test:integration": "aw puppet --testExt '*.int.js' --glob 'test/integration/**/*.int.js'"
}, },
"devDependencies": { "devDependencies": {
"@after-work.js/aw": "^6.0.3", "@after-work.js/aw": "^6.0.3",

View File

@@ -2,7 +2,8 @@ const serve = require('@nebula.js/cli-serve'); // eslint-disable-line
let s; let s;
before(async () => { before(async function setup() {
this.timeout(15000);
s = await serve({ s = await serve({
open: false, open: false,
}); });

View File

@@ -9,8 +9,12 @@ describe('interaction', () => {
await page.click('rect[data-label="K"]'); await page.click('rect[data-label="K"]');
await page.click('rect[data-label="S"]'); await page.click('rect[data-label="S"]');
await page.waitForSelector('button[title="Confirm selection"]');
await page.click('button[title="Confirm selection"]'); await page.click('button[title="Confirm selection"]');
await page.waitFor(100); // wait a bit to make sure websocket traffic has gone through
const rects = await page.$$eval('rect[data-label]', sel => sel.map(r => r.getAttribute('data-label'))); const rects = await page.$$eval('rect[data-label]', sel => sel.map(r => r.getAttribute('data-label')));
expect(rects).to.eql(['K', 'S']); expect(rects).to.eql(['K', 'S']);
}); });

View File

@@ -18,7 +18,7 @@
"build": "nebula build", "build": "nebula build",
"lint": "eslint src", "lint": "eslint src",
"start": "nebula serve", "start": "nebula serve",
"test:integration": "aw puppet --testExt '*.int.js' --glob 'test/integration/**/*.int.js' --chrome.headless true --chrome.slowMo 10" "test:integration": "aw puppet --testExt '*.int.js' --glob 'test/integration/**/*.int.js'"
}, },
"devDependencies": { "devDependencies": {
"@after-work.js/aw": "^6.0.3", "@after-work.js/aw": "^6.0.3",

View File

@@ -2,7 +2,8 @@ const serve = require('@nebula.js/cli-serve'); // eslint-disable-line
let s; let s;
before(async () => { before(async function setup() {
this.timeout(15000); // to allow time for the server to start
s = await serve({ s = await serve({
build: false, build: false,
}); });