diff --git a/app/data/dataFileNames.json b/app/data/dataFileNames.json index 467fdd0..96ecfbf 100644 --- a/app/data/dataFileNames.json +++ b/app/data/dataFileNames.json @@ -1 +1 @@ -{"main":"firstModule.json","others":["RfEsZpNK.791f2cda554d18a95fc3734bbe65f88b.json","sArkxak3.bda40d280ffd6baa48d38117905bd5ab.json","2qVqSwId.6c0be3e0610de0d44ac1b294da16d4fd.json","vQrgRbIS.80594ce9bebcfa2809538046c647dde3.json","U77gcZNf.8857dc7179e0813487e12b85611b3b9b.json","RqGMsFtz.aeed2715e184fe51657af9db9ba6f965.json"]} \ No newline at end of file +{"main":"firstModule.json","others":["RfEsZpNK.791f2cda554d18a95fc3734bbe65f88b.json","sArkxak3.bda40d280ffd6baa48d38117905bd5ab.json","2qVqSwId.b86995173d611d4e9c0524bce3ecf6e5.json","vQrgRbIS.80594ce9bebcfa2809538046c647dde3.json","U77gcZNf.8857dc7179e0813487e12b85611b3b9b.json","RqGMsFtz.aeed2715e184fe51657af9db9ba6f965.json"]} \ No newline at end of file diff --git a/app/data/store.js b/app/data/store.js index 8996ae7..16ed5a9 100644 --- a/app/data/store.js +++ b/app/data/store.js @@ -27,6 +27,7 @@ import { ANALYTICS_STRINGS, EVENTS, SCORES, + INHERITED_KEYS, } from '../utils/constants'; import logTiming from '../utils/logTiming'; @@ -99,6 +100,14 @@ const store = { }); }, + computeInheritRule(newScoreKey, oldScoreKey) { + if (INHERITED_KEYS.includes(newScoreKey)) { + return [(item) => true, newScoreKey]; + } else if (INHERITED_KEYS.includes(oldScoreKey)) { + return [(item) => item.scoreKey === oldScoreKey, SCORES.LEVEL_0.key]; + } + }, + getItem(idOrItem) { if (typeof idOrItem === `string`) { return this.itemCache[idOrItem]; @@ -197,20 +206,25 @@ const store = { Object.assign(item, data); // gasp, mutability + if (saveToDisk) { + diskStore.setItem(item.id, data); + } + if (!updateDom) return; const nextItemState = serializeItemState(item); + this.updateItemDom(item, prevItemState !== nextItemState, scoreChanged, options); + }, + updateItemDom(item, renderItem, renderScore, options = {}) { // potentially trigger a re-render of the item - if (item.visible && prevItemState !== nextItemState) { + if (item.visible && renderItem) { this.triggerListener(item.id); // TODO (davidg): `ROW-${item.id}` } // potentially trigger a re-render of the score bar - if (scoreChanged) { - if (saveToDisk) diskStore.setItem(item.id, data); - - if (this.selectedItem && this.selectedItem.id === idOrItem.id) { + if (renderScore) { + if (this.selectedItem && this.selectedItem.id === item.id) { this.triggerListener(EVENTS.SCORE_CHANGED); // updates the score bar } } @@ -221,15 +235,56 @@ const store = { if (!item) return; const updateDom = (typeof options.updateDom !== `undefined`) ? options.updateDom : true; + const oldScoreKey = item.scoreKey; + // caution: update the score summary before updating the item - const updatedItems = this.updateScoreSummary(item, scoreKey, item.scoreKey); + const updatedItems = this.updateScoreSummary(item, scoreKey, oldScoreKey); this.updateItem(item, { scoreKey }, options); + const inheritRule = this.computeInheritRule(scoreKey, oldScoreKey); + + if (inheritRule) { + const dirtyChildren = []; + + const updateChildren = (parent, test) => { + const children = this.getChildrenOf(parent.id); + if (!children) return; + + children.forEach((child) => { + if (test(child)) { + dirtyChildren.push(child); + } + + updateChildren(child, test); + }); + }; + + updateChildren(item, inheritRule[0]); + + const optionsNoDom = Object.assign({}, options); + optionsNoDom.updateDom = false; + + const updateChild = (child) => { + this.updateItem(child, { scoreKey: inheritRule[1] }, optionsNoDom); + }; + const updateChildDom = (child) => { + this.updateItemDom(child, true, true, options); + }; + + dirtyChildren.forEach(updateChild); + + const visibleChildren = dirtyChildren.filter((child) => child.visible); + visibleChildren.forEach(updateChildDom); + } + if (updateDom) { - updatedItems.forEach((updatedItem) => { + const updateItem = (updatedItem) => { this.triggerListener(`PIE-${updatedItem.id}`); - }); + }; + + const visibleItems = updatedItems.filter((item) => item.visible); + visibleItems.forEach(updateItem); } }, @@ -241,23 +296,56 @@ const store = { oldScoreKey = oldScoreKey || SCORES.LEVEL_0.key; const updatedItems = []; - const updateParentScore = (child) => { - const parent = this.getParent(child); - if (!parent) return; // we're at the top + const updateSummary = (parent, inheritRule) => { + const children = this.getChildrenOf(parent.id); + if (!children) return; // we're at the bottom updatedItems.push(parent); this.scoreSummary[parent.id] = this.scoreSummary[parent.id] || {}; - this.scoreSummary[parent.id][newScoreKey] = this.scoreSummary[parent.id][newScoreKey] || 0; - this.scoreSummary[parent.id][newScoreKey] += 1; + Object.keys(SCORES).forEach((scoreKey) => { + this.scoreSummary[parent.id][scoreKey] = children + .map((child) => { + this.scoreSummary[child.id] = this.scoreSummary[child.id] || {}; + const scoreSummary = this.scoreSummary[child.id][scoreKey] || 0; + let newValue = child.scoreKey || SCORES.LEVEL_0.key; + + if (child === item) { + newValue = newScoreKey; + } else if (inheritRule) { + if (inheritRule[0](child)) { + newValue = inheritRule[1]; + } + } + + return newValue === scoreKey ? scoreSummary + 1 : scoreSummary; + }) + .reduce((a, b) => a + b, 0); + }); + }; + const updateChildrenScore = (parent, inheritRule) => { + const children = this.getChildrenOf(parent.id); + if (!children) return; // we're at the bottom - if (!newItem) { - this.scoreSummary[parent.id][oldScoreKey] -= 1; - } + children.forEach((child) => { + updateChildrenScore(child, inheritRule); + }); + + updateSummary(parent, inheritRule); + }; + const updateParentScore = (child) => { + const parent = this.getParent(child); + if (!parent) return; // we're at the top + updateSummary(parent); updateParentScore(parent); }; - // we don't update the item directly, only its ancestors + const inheritRule = this.computeInheritRule(newScoreKey, oldScoreKey); + + if (inheritRule) { + updateChildrenScore(item, inheritRule); + } + updateParentScore(item); return updatedItems; diff --git a/app/utils/constants.js b/app/utils/constants.js index 593753b..5b9ff56 100644 --- a/app/utils/constants.js +++ b/app/utils/constants.js @@ -131,6 +131,8 @@ export const SCORES = { }, }; +export const INHERITED_KEYS = [SCORES.LEVEL_1.key, SCORES.LEVEL_4.key]; + export const KEYS = { DOWN: 40, ENTER: 13,