Beispiel #1
0
    [actions.removePage]: (workpadState, { payload }) => {
      const curIndex = workpadState.page;
      const delIndex = getPageIndexById(workpadState, payload);
      if (delIndex >= 0) {
        let newState = del(workpadState, `pages.${delIndex}`);
        const router = routerProvider();
        const wasSelected = curIndex === delIndex;
        const wasOnlyPage = newState.pages.length === 0;
        const newSelectedPage = curIndex >= delIndex ? curIndex - 1 : curIndex;

        // if we removed the only page, create a new empty one
        if (wasOnlyPage) {
          newState = addPage(newState);
        }

        if (wasOnlyPage || wasSelected) {
          // if we removed the only page or the selected one, select the first one
          newState = set(newState, 'page', 0);
        } else {
          // set the adjusted selected page on new state
          newState = set(newState, 'page', newSelectedPage);
        }

        // changes to the page require navigation
        router.navigateTo('loadWorkpad', { id: newState.id, page: newState.page + 1 });

        return newState;
      }
    },
Beispiel #2
0
  ({ dispatch, getState }, index, ast, element, pageId) => {
    // invalidate cached context for elements after this index
    dispatch(flushContextAfterIndex({ elementId: element.id, index }));

    const newElement = set(element, ['ast', 'chain', index], ast);
    const newAst = get(newElement, 'ast');

    // fetch renderable using existing context, if available (value is null if not cached)
    const { index: contextIndex, context: contextValue } = getSiblingContext(
      getState(),
      element.id,
      index - 1
    );

    // if we have a cached context, update the expression, but use cache when updating the renderable
    if (contextValue) {
      // set the expression, but skip the fetchRenderable step
      dispatch(setAst(newAst, element, pageId, false));

      // use context when updating the expression, it will be passed to the intepreter
      const partialAst = {
        ...newAst,
        chain: newAst.chain.filter((exp, i) => {
          if (contextValue) {
            return i > contextIndex;
          }
          return i >= index;
        }),
      };
      return dispatch(fetchRenderableWithContext(newElement, partialAst, contextValue));
    }

    // if no cached context, update the ast like normal
    dispatch(setAst(newAst, element, pageId));
  }
			paths.forEach(path => {
				const mount = mounts[path];

				if (!mount.reducer) {
					// if `mount` has been called for a path, but the corresponding
					// mounted store creator *not* called, then no reducer will be
					// registered, so skip that path
					return;
				}

				updateMountCache(path);

				const newMergedState =
					mount.reducer.call(null, mount.cache.mergedState, action);

				if (newMergedState !== mount.cache.mergedState) {
					// FIXME: check that viewed state is not modified
					// FIXME: test that asserts that removed viewedState is reapplied
					mount.cache.mergedState =
						_merge(newMergedState, mount.cache.viewedState);
					mount.cache.ownState =
						_omit(newMergedState, Object.keys(mount.viewedStateSpec));
					newState = immutable.set(newState, path, mount.cache.ownState);
				}
			});
		t.test('dispatch', t => {
			const action = {
				type: 'STARE',
				payload: '<3<3<3'
			};
			const newState = immutable.set(initialState, 'bears.stare', true);

			reducer.reset();
			returnValue = newState;
			store.dispatch(action);

			t.ok(reducer.calledOnce, 'reducer called when action dispatched');

			t.same(
				reducer.firstCall.args[0],
				initialState,
				'reducer called with an object that looks like initial state'
			);

			t.strictEqual(
				reducer.firstCall.args[1],
				action,
				'reducer called with dispatched action'
			);

			t.strictEqual(
				store.getState(),
				newState,
				'getState returns new state'
			);

			t.end();
		});
						(state, action) => {
							if (action.type === 'MUTATE_ROOT') {
								state = immutable.set(state, 'rootOwn', 'new root');
							}

							return state;
						},
						(state, action) => {
							if (action.type === 'MUTATE_HOST') {
								return immutable.set(state, 'hostOwn', 'hostnew');
							}

							return state;
						},
					const hostReducer = sinon.spy((state, action) => {
						if (action.type === 'MUTATE_HOST') {
							state = immutable.set(state, 'hostOwn', 'new host');
						}

						return state;
					});
				const childReducer = sinon.spy((state, action) => {
					if (action.type === 'MUTATE_CHILD') {
						state = immutable.set(state, 'childOwn', 'new child');
					}

					return state;
				});
Beispiel #9
0
    [actions.movePage]: (workpadState, { payload }) => {
      const { id, position } = payload;
      const pageIndex = getPageIndexById(workpadState, id);
      const newIndex = pageIndex + position;

      // TODO: do something better when given an invalid page id
      if (pageIndex < 0) {
        return workpadState;
      }

      // don't move pages past the first or last position
      if (newIndex < 0 || newIndex >= workpadState.pages.length) {
        return workpadState;
      }

      // remove and re-insert the page
      const page = { ...workpadState.pages[pageIndex] };
      let newState = insert(del(workpadState, `pages.${pageIndex}`), 'pages', page, newIndex);

      // adjust the selected page index and return the new state
      const selectedId = workpadState.pages[workpadState.page].id;
      const newSelectedIndex = newState.pages.findIndex(page => page.id === selectedId);
      newState = set(newState, 'page', newSelectedIndex);

      // changes to the page require navigation
      const router = routerProvider();
      router.navigateTo('loadWorkpad', { id: newState.id, page: newState.page + 1 });

      return newState;
    },
Beispiel #10
0
 [createAsset]: (assetState, { payload }) => {
   const asset = {
     id: getId('asset'),
     '@created': new Date().toISOString(),
     ...payload,
   };
   return set(assetState, asset.id, asset);
 },
		t.test('replaceReducer', t => {
			const newState = immutable.set(initialState, 'cousins', {
				care: 'true',
				call: 'false'
			});
			const newReducer = sinon.stub().returns(newState);

			reducer.reset();
			store.replaceReducer(newReducer);

			t.ok(!reducer.called, 'old reducer not called');
			t.ok(newReducer.calledOnce, 'new reducer called once');
			t.same(
				newReducer.firstCall.args[0],
				initialState,
				'new reducer called with an object that looks like initial state'
			);
			t.match(
				newReducer.firstCall.args[1],
				{type: '@@redux/INIT'},
				'action was @@redux/INIT'
			);

			t.strictEqual(
				store.getState(),
				newState,
				'getState returns new state'
			);

			newReducer.reset();
			const action = {
				type: 'STARE',
				payload: '<3<3<3'
			};
			const newerState = immutable(newState)
				.set('bears.stare', true)
				.set('cousins.call', true)
				.value();
			newReducer.returns(newerState);
			store.dispatch(action);

			t.ok(!reducer.called, 'old reducer not called');
			t.ok(newReducer.calledOnce, 'new reducer called once');
			t.ok(
				newReducer.calledWithExactly(newState, action),
				'reducer called with previous state and action'
			);

			t.strictEqual(
				store.getState(),
				newerState,
				'getState returns newer state'
			);

			t.end();
		});
Beispiel #12
0
export const addLinksToState = (state, links, options) => {
  if (options === undefined || options.indexLinks === undefined) {
    return state;
  }

  const indexLinkName = options.indexLinks;
  const newState = imm.set(state, `links.${indexLinkName}`, links);

  return newState;
};
Beispiel #13
0
export const setArgumentAtIndex = createThunk('setArgumentAtIndex', ({ dispatch }, args) => {
  const { index, argName, value, valueIndex, element, pageId } = args;
  const selector = ['ast', 'chain', index, 'arguments', argName];
  if (valueIndex != null) {
    selector.push(valueIndex);
  }

  const newElement = set(element, selector, value);
  const newAst = get(newElement, ['ast', 'chain', index]);
  dispatch(setAstAtIndex(index, newAst, element, pageId));
});
Beispiel #14
0
const updateRepoStarCount = (id, viewerHadStarred, client, mutationResult) => {
  const cacheId = `Repository:${id}`;
  const repo = client.readFragment({
    id: cacheId,
    fragment: REPOSITORY_FRAGMENT,
  });
  const newStarCount = repo.stargazers.totalCount + (viewerHadStarred ? -1 : 1);
  const data = immutable.set(repo, 'stargazers.totalCount', newStarCount);

  client.writeFragment({
    id: cacheId,
    fragment: REPOSITORY_FRAGMENT,
    data,
  });
};
		/*
		 * Calculate and return the viewed state for a mounted path
		 *
		 * This function calculates the viewed state for a mounted path. To do so,
		 * it starts with the currently cached viewed state if it exists, and then
		 * recalculates each value in the viewed state spec and compares it to the
		 * cached value. If different, it generates a new viewed state with the new
		 * value.
		 *
		 * When finished the new viewed state (or the cached viewed state if none of
		 * the viewed values have changed) is returned. The cached viewed state
		 * is *not* changed.
		 *
		 * @param {string} path - the mounted path to calculate viewed state for
		 *
		 * @returns {Object} the calculated viewed state
		 */
		function getViewedState(path) {
			const mount = mounts[path];
			const viewedStateSpec = mount.viewedStateSpec;
			const hostMergedState = mount.host === null ?
				rootState :
				mounts[mount.host].cache.mergedState;
			let viewedState = mount.cache.viewedState || {};

			for (let spec in viewedStateSpec) {
				let value = viewedStateSpec[spec](hostMergedState);
				if (viewedState[spec] !== value) {
					viewedState = immutable.set(viewedState, spec, value);
				}
			}

			return viewedState;
		}
Beispiel #16
0
  Object.keys(rels).forEach(relKey => {
    if (!hasOwnProperties(rels[relKey], ['data', 'type'])) {
      return;
    }

    const entityPath = [rels[relKey].data.type, 'data'];

    if (!hasOwnProperties(newState, entityPath)) {
      return;
    }

    const updateReverseRelationship = makeUpdateReverseRelationship(
      resource, rels[relKey]
    );

    newState = imm.set(
      newState,
      entityPath,
      updateReverseRelationship(newState[rels[relKey].data.type].data)
    );
  });
Beispiel #17
0
function setPageIndex(workpadState, index) {
  if (index < 0 || !workpadState.pages[index]) return workpadState;
  return set(workpadState, 'page', index);
}
Beispiel #18
0
 [actions.setFullscreen]: (transientState, { payload }) => {
   return set(transientState, 'fullscreen', Boolean(payload));
 },
Beispiel #19
0
 [actions.setCanUserWrite]: (transientState, { payload }) => {
   return set(transientState, 'canUserWrite', Boolean(payload));
 },
Beispiel #20
0
 [restoreHistory]: transientState => set(transientState, 'resolvedArgs', {}),
Beispiel #21
0
 [actions.stylePage]: (workpadState, { payload }) => {
   const pageIndex = workpadState.pages.findIndex(page => page.id === payload.pageId);
   return set(workpadState, ['pages', pageIndex, 'style'], payload.style);
 },
Beispiel #22
0
reducers[ WOOCOMMERCE_SERVICES_SERVICE_SETTINGS_UPDATE_FIELD ] = ( state, action ) => {
	return objectPath.set( state, action.path, action.value );
};
		const reducer = (state, action) => {
			let newState = rootReducer(state, action);

			// update cached ownState of root store
			rootState = newState;

			// FIXME: this reducer will also be responsible for handling queries
			// Specifically, it should update the relevant viewedStateSpec

			// FIXME: update state *before* calling root reducer
			if (action.type === MOUNT) {
				// add the initial own state of the mounted store to the root store's
				// state
				newState = immutable.set(
					newState,
					action.payload.path,
					action.payload.initialState
				);

				// prime the cached state for the mounted store
				updateCachedState(
					action.payload.path,
					_get(newState, action.payload.path)
				);

				// bypass mounted reducers for this action
				return newState;
			} else if (action.type === UNMOUNT) {
				// remove the unmounted store's own state from the root store's state
				newState = immutable.del(newState, action.payload.path);

				delete mounts[action.payload.path];
			} else if (action.type === QUERY_RESULT) {
				let mount = mounts[action.payload.path];
				let queriedStateSpec = _mapValues(
					action.payload.result,
					result => () => _get(rootState, result)
				);

				mount.viewedStateSpec =
					_assign({}, mount.viewedStateSpec, queriedStateSpec);
			}

			// FIXME: calculate this array at mount/unmount time
			// iterate over mounted reducers, breadth-first
			let paths = [];
			for (const path in mounts) {
				paths.push(path.split('.'));
			}
			paths = paths.sort(
				(pathA, pathB) => {
					return pathA.length - pathB.length;
				})
				.map(path => path.join('.'));

			function updateMountCache(path) {
				const mount = mounts[path];

				const ownState = _get(newState, path);
				const viewedState = getViewedState(path);

				if (ownState !== mount.cache.ownState ||
					viewedState !== mount.cache.viewedState
				) {
					// either the mount's own state or its viewed state have changed,
					// so recalculate its merged state
					updateCachedState(path, ownState);
				}
			}

			paths.forEach(path => {
				const mount = mounts[path];

				if (!mount.reducer) {
					// if `mount` has been called for a path, but the corresponding
					// mounted store creator *not* called, then no reducer will be
					// registered, so skip that path
					return;
				}

				updateMountCache(path);

				const newMergedState =
					mount.reducer.call(null, mount.cache.mergedState, action);

				if (newMergedState !== mount.cache.mergedState) {
					// FIXME: check that viewed state is not modified
					// FIXME: test that asserts that removed viewedState is reapplied
					mount.cache.mergedState =
						_merge(newMergedState, mount.cache.viewedState);
					mount.cache.ownState =
						_omit(newMergedState, Object.keys(mount.viewedStateSpec));
					newState = immutable.set(newState, path, mount.cache.ownState);
				}
			});

			// update cached state for all mounts
			paths.forEach(updateMountCache);

			return newState;
		};
Beispiel #24
0
 [setAutoplayInterval]: (transientState, { payload }) => {
   return set(transientState, 'autoplay.interval', Number(payload) || 0);
 },
Beispiel #25
0
 [actions.setPageTransition]: (workpadState, { payload }) => {
   const pageIndex = workpadState.pages.findIndex(page => page.id === payload.pageId);
   return set(workpadState, ['pages', pageIndex, 'transition'], payload.transition);
 },
Beispiel #26
0
 [enableAutoplay]: (transientState, { payload }) => {
   return set(transientState, 'autoplay.enabled', Boolean(payload) || false);
 },
Beispiel #27
0
 [transientActions.setElementStats]: (transientState, { payload }) => {
   return set(transientState, 'elementStats', payload);
 },
Beispiel #28
0
 [transientActions.setFirstLoad]: (transientState, { payload }) => {
   return set(transientState, 'isFirstLoad', Boolean(payload));
 },
Beispiel #29
0
export const updateOrInsertResource = (state, resource) => {
  if (typeof resource !== 'object') {
    return state;
  }

  let newState = state;
  const updatePath = [resource.type, 'data'];

  if (stateContainsResource(state, resource)) {
    const resources = state[resource.type].data;
    const idx = resources.findIndex(item => item.id === resource.id);

    const relationships = {};
    for (const relationship in resources[idx].relationships) {
      if (!hasOwnProperties(resource, ['relationships', relationship, 'data'])) {
        relationships[relationship] = resources[idx].relationships[relationship];
      }
    }
    if (!resource.hasOwnProperty('relationships')) {
      Object.assign(resource, { relationships });
    } else {
      Object.assign(resource.relationships, relationships);
    }

    if (!equal(resources[idx], resource)) {
      newState = imm.set(newState, updatePath.concat(idx), resource);
    }
  } else {
    newState = imm.push(newState, updatePath, resource);
  }

  const rels = resource.relationships;

  if (!rels) {
    return newState;
  }

  Object.keys(rels).forEach(relKey => {
    if (!hasOwnProperties(rels[relKey], ['data', 'type'])) {
      return;
    }

    const entityPath = [rels[relKey].data.type, 'data'];

    if (!hasOwnProperties(newState, entityPath)) {
      return;
    }

    const updateReverseRelationship = makeUpdateReverseRelationship(
      resource, rels[relKey]
    );

    newState = imm.set(
      newState,
      entityPath,
      updateReverseRelationship(newState[rels[relKey].data.type].data)
    );
  });

  return newState;
};
Beispiel #30
0
const setPageIndex = (workpadState, index) =>
  index < 0 || !workpadState.pages[index] || getSelectedPageIndex(workpadState) === index
    ? workpadState
    : set(workpadState, 'page', index);