blob: 0e360faa27b9558e0e05e737c37d3bd439cdbb92 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import {Action, ActionReducer} from '@ngrx/store';
import {createSelector} from 'reselect';
import {RoutePayload} from './route-payload';
export interface Resource {
identifier: string;
}
export interface LoadResourcePayload {
resource: any;
}
export interface SelectResourcePayload {
selectedId: string;
}
export interface CreateResourceSuccessPayload extends RoutePayload {
resource: any;
}
export interface UpdateResourceSuccessPayload extends RoutePayload {
resource: any;
}
export interface DeleteResourceSuccessPayload extends RoutePayload {
resource: any;
}
export interface ResourceState {
ids: string[];
entities: { [id: string]: any };
selectedId: string | null;
loadedAt: { [id: string]: number };
}
const initialState: ResourceState = {
ids: [],
entities: {},
loadedAt: {},
selectedId: null
};
export const createResourceReducer =
(resourceId: string, reducer?: ActionReducer<ResourceState>, identifierName: string = 'identifier') => {
const identifier = (resource: any) => resource[identifierName];
return function (state: ResourceState = initialState, action: Action): ResourceState {
switch (action.type) {
case `[${resourceId}] Load`: {
const resource = action.payload.resource;
const newIds = state.ids.filter(id => id !== identifier(resource));
return {
ids: [...newIds, identifier(resource)],
entities: Object.assign({}, state.entities, {
[identifier(resource)]: resource
}),
selectedId: state.selectedId,
loadedAt: Object.assign({}, state.entities, {
[identifier(resource)]: Date.now()
})
};
}
case `[${resourceId}] Select`: {
return Object.assign({}, state, {
selectedId: action.payload
});
}
case `[${resourceId}] Create Success`: {
const resource = action.payload.resource;
return {
ids: [...state.ids, identifier(resource)],
entities: Object.assign({}, state.entities, {
[identifier(resource)]: resource
}),
selectedId: state.selectedId,
loadedAt: state.loadedAt
};
}
case `[${resourceId}] Update Success`: {
const resource = action.payload.resource;
return {
ids: state.ids,
entities: Object.assign({}, state.entities, {
[identifier(resource)]: resource
}),
selectedId: state.selectedId,
loadedAt: state.loadedAt
};
}
case `[${resourceId}] Delete Success`: {
const resource = action.payload.resource;
const newIds = state.ids.filter(id => id !== identifier(resource));
const newEntities = newIds.reduce((entities: { [id: string]: any }, id: string) => {
const entity = state.entities[id];
return Object.assign(entities, {
[identifier(entity)]: entity
});
}, {});
const newLoadedAt = newIds.reduce((entities: { [id: string]: any }, id: string) => {
const loadedAt = state.loadedAt[id];
return Object.assign(entities, {
[id]: loadedAt
});
}, {});
return {
ids: [...newIds],
entities: newEntities,
loadedAt: newLoadedAt,
selectedId: state.selectedId,
};
}
default: {
// delegate to wrapped reducer
if (reducer) {
return reducer(state, action);
}
return state;
}
}
};
};
export const getResourceEntities = (cacheState: ResourceState) => cacheState.entities;
export const getResourceLoadedAt = (cacheState: ResourceState) => cacheState.loadedAt;
export const getResourceIds = (cacheState: ResourceState) => cacheState.ids;
export const getResourceSelectedId = (cacheState: ResourceState) => cacheState.selectedId;
export const getResourceSelected = createSelector(getResourceEntities, getResourceSelectedId, (entities, selectedId) => {
return entities[selectedId];
});
export const getResourceAll = createSelector(getResourceEntities, getResourceIds, (entities, ids) => {
return ids.map(id => entities[id]);
});