mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-17 06:37:23 +01:00
client: namespaces for actions and getteters
This commit is contained in:
@@ -15,8 +15,10 @@
|
|||||||
"aws-sdk": "^2.0.25",
|
"aws-sdk": "^2.0.25",
|
||||||
"babel-core": "^6.0.0",
|
"babel-core": "^6.0.0",
|
||||||
"babel-loader": "^6.0.0",
|
"babel-loader": "^6.0.0",
|
||||||
|
"babel-plugin-syntax-async-functions": "^6.13.0",
|
||||||
"babel-plugin-transform-async-to-module-method": "^6.8.0",
|
"babel-plugin-transform-async-to-module-method": "^6.8.0",
|
||||||
"babel-plugin-transform-object-rest-spread": "^6.16.0",
|
"babel-plugin-transform-object-rest-spread": "^6.16.0",
|
||||||
|
"babel-plugin-transform-regenerator": "^6.16.1",
|
||||||
"babel-polyfill": "^6.6.1",
|
"babel-polyfill": "^6.6.1",
|
||||||
"babel-preset-es2015": "^6.6.0",
|
"babel-preset-es2015": "^6.6.0",
|
||||||
"babel-register": "^6.6.0",
|
"babel-register": "^6.6.0",
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
{
|
{
|
||||||
"presets": ["es2015"],
|
"presets": ["es2015"],
|
||||||
"plugins": ["transform-object-rest-spread"],
|
"plugins": [
|
||||||
|
"transform-object-rest-spread",
|
||||||
|
"syntax-async-functions",
|
||||||
|
"transform-regenerator",
|
||||||
|
],
|
||||||
"comments": false
|
"comments": false
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { userGems } from 'client/store/getters';
|
import { gems as userGems } from 'client/store/getters/user';
|
||||||
|
|
||||||
describe('userGems getter', () => {
|
describe('userGems getter', () => {
|
||||||
it('returns the user\'s gems', () => {
|
it('returns the user\'s gems', () => {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import storeInjector from 'inject?-vue!client/store';
|
import storeInjector from 'inject?-vue!client/store';
|
||||||
import { mapState, mapGetters, mapActions } from 'client/store';
|
import { mapState, mapGetters, mapActions } from 'client/store';
|
||||||
|
import { flattenAndNamespace } from 'client/store/helpers/internals';
|
||||||
|
|
||||||
describe('Store', () => {
|
describe('Store', () => {
|
||||||
let injectedStore;
|
let injectedStore;
|
||||||
@@ -14,11 +15,25 @@ describe('Store', () => {
|
|||||||
computedName ({ state }) {
|
computedName ({ state }) {
|
||||||
return `${state.name} computed!`;
|
return `${state.name} computed!`;
|
||||||
},
|
},
|
||||||
|
...flattenAndNamespace({
|
||||||
|
nested: {
|
||||||
|
computedName ({ state }) {
|
||||||
|
return `${state.name} computed!`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
'./actions': {
|
'./actions': {
|
||||||
getName ({ state }, ...args) {
|
getName ({ state }, ...args) {
|
||||||
return [state.name, ...args];
|
return [state.name, ...args];
|
||||||
},
|
},
|
||||||
|
...flattenAndNamespace({
|
||||||
|
nested: {
|
||||||
|
getName ({ state }, ...args) {
|
||||||
|
return [state.name, ...args];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
}).default;
|
}).default;
|
||||||
});
|
});
|
||||||
@@ -41,17 +56,29 @@ describe('Store', () => {
|
|||||||
injectedStore.state.name = 'test updated';
|
injectedStore.state.name = 'test updated';
|
||||||
});
|
});
|
||||||
|
|
||||||
it('supports getters', () => {
|
describe('getters', () => {
|
||||||
expect(injectedStore.getters.computedName).to.equal('test computed!');
|
it('supports getters', () => {
|
||||||
injectedStore.state.name = 'test updated';
|
expect(injectedStore.getters.computedName).to.equal('test computed!');
|
||||||
expect(injectedStore.getters.computedName).to.equal('test updated computed!');
|
injectedStore.state.name = 'test updated';
|
||||||
|
expect(injectedStore.getters.computedName).to.equal('test updated computed!');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('supports nested getters', () => {
|
||||||
|
expect(injectedStore.getters['nested:computedName']).to.equal('test computed!');
|
||||||
|
injectedStore.state.name = 'test updated';
|
||||||
|
expect(injectedStore.getters['nested:computedName']).to.equal('test updated computed!');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('actions', () => {
|
describe('actions', () => {
|
||||||
it('can be dispatched', () => {
|
it('can dispatch an action', () => {
|
||||||
expect(injectedStore.dispatch('getName', 1, 2, 3)).to.deep.equal(['test', 1, 2, 3]);
|
expect(injectedStore.dispatch('getName', 1, 2, 3)).to.deep.equal(['test', 1, 2, 3]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('can dispatch a nested action', () => {
|
||||||
|
expect(injectedStore.dispatch('nested:getName', 1, 2, 3)).to.deep.equal(['test', 1, 2, 3]);
|
||||||
|
});
|
||||||
|
|
||||||
it('throws an error is the action doesn\'t exists', () => {
|
it('throws an error is the action doesn\'t exists', () => {
|
||||||
expect(() => injectedStore.dispatched('wrong')).to.throw;
|
expect(() => injectedStore.dispatched('wrong')).to.throw;
|
||||||
});
|
});
|
||||||
@@ -116,5 +143,26 @@ describe('Store', () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('flattenAndNamespace', () => {
|
||||||
|
let result = flattenAndNamespace({
|
||||||
|
nested: {
|
||||||
|
computed ({ state }, ...args) {
|
||||||
|
return [state.name, ...args];
|
||||||
|
},
|
||||||
|
getName ({ state }, ...args) {
|
||||||
|
return [state.name, ...args];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
nested2: {
|
||||||
|
getName ({ state }, ...args) {
|
||||||
|
return [state.name, ...args];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(Object.keys(result).length).to.equal(3);
|
||||||
|
expect(Object.keys(result).sort()).to.deep.equal(['nested2:getName', 'nested:computed', 'nested:getName']);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
{
|
{
|
||||||
"presets": ["es2015"],
|
"presets": ["es2015"],
|
||||||
"plugins": ["transform-object-rest-spread"],
|
"plugins": [
|
||||||
|
"transform-object-rest-spread",
|
||||||
|
"syntax-async-functions",
|
||||||
|
"transform-regenerator",
|
||||||
|
],
|
||||||
}
|
}
|
||||||
@@ -213,7 +213,9 @@ import { mapState, mapGetters } from '../store';
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters(['userGems']),
|
...mapGetters({
|
||||||
|
userGems: 'user:gems',
|
||||||
|
}),
|
||||||
...mapState(['user']),
|
...mapState(['user']),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
import tasks from './tasks';
|
import { flattenAndNamespace } from '../helpers/internals';
|
||||||
import user from './user';
|
import * as tasks from './tasks';
|
||||||
|
import * as user from './user';
|
||||||
|
|
||||||
const actions = {
|
// Actions should be named as 'actionName' and can be accessed as 'namespace.actionName'
|
||||||
tasks,
|
// Example: fetch in user.js -> 'user.fetch'
|
||||||
|
|
||||||
|
const actions = flattenAndNamespace({
|
||||||
user,
|
user,
|
||||||
};
|
tasks,
|
||||||
|
});
|
||||||
|
|
||||||
export default actions;
|
export default actions;
|
||||||
@@ -1,15 +1,6 @@
|
|||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
|
||||||
const actions = {};
|
export async function fetchUserTasks (store) {
|
||||||
|
let response = await Vue.http.get('/api/v3/tasks/user');
|
||||||
actions.fetchUserTasks = function fetchUserTasks (store) {
|
store.state.tasks = response.body.data;
|
||||||
let promise = Vue.http.get('/api/v3/tasks/user');
|
}
|
||||||
|
|
||||||
promise.then((response) => {
|
|
||||||
store.state.tasks = response.body.data;
|
|
||||||
});
|
|
||||||
|
|
||||||
return promise;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default actions;
|
|
||||||
@@ -1,15 +1,6 @@
|
|||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
|
||||||
const actions = {};
|
export async function fetch (store) { // eslint-disable-line no-shadow
|
||||||
|
let response = await Vue.http.get('/api/v3/user');
|
||||||
actions.fetch = function fetchUser (store) {
|
store.state.user = response.body.data;
|
||||||
let promise = Vue.http.get('/api/v3/user');
|
}
|
||||||
|
|
||||||
promise.then((response) => {
|
|
||||||
store.state.user = response.body.data;
|
|
||||||
});
|
|
||||||
|
|
||||||
return promise;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default actions;
|
|
||||||
@@ -1,3 +1,11 @@
|
|||||||
export function userGems (store) {
|
import { flattenAndNamespace } from '../helpers/internals';
|
||||||
return store.state.user.balance * 4;
|
import * as user from './user';
|
||||||
}
|
|
||||||
|
// Getters should be named as 'getterName' and can be accessed as 'namespace.getterName'
|
||||||
|
// Example: gems in user.js -> 'user.gems'
|
||||||
|
|
||||||
|
const getters = flattenAndNamespace({
|
||||||
|
user,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default getters;
|
||||||
3
website/client/store/getters/user.js
Normal file
3
website/client/store/getters/user.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export function gems (store) {
|
||||||
|
return store.state.user.balance * 4;
|
||||||
|
}
|
||||||
32
website/client/store/helpers/internals.js
Normal file
32
website/client/store/helpers/internals.js
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/* Flatten multiple objects into a single, namespaced object.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
getters
|
||||||
|
user
|
||||||
|
gems
|
||||||
|
tasks
|
||||||
|
...
|
||||||
|
tasks
|
||||||
|
todos
|
||||||
|
dailys
|
||||||
|
...
|
||||||
|
|
||||||
|
Result:
|
||||||
|
getters
|
||||||
|
user.gems
|
||||||
|
user.tasks
|
||||||
|
tasks.todos
|
||||||
|
tasks.dailys
|
||||||
|
*/
|
||||||
|
export function flattenAndNamespace (namespaces) {
|
||||||
|
let result = {};
|
||||||
|
|
||||||
|
Object.keys(namespaces).forEach(namespace => {
|
||||||
|
Object.keys(namespaces[namespace]).forEach(itemName => {
|
||||||
|
result[`${namespace}:${itemName}`] = namespaces[namespace][itemName];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import state from './state';
|
import state from './state';
|
||||||
import actions from './actions';
|
import actions from './actions';
|
||||||
import * as getters from './getters';
|
import getters from './getters';
|
||||||
import { get } from 'lodash';
|
|
||||||
|
|
||||||
// Central application store for Habitica
|
// Central application store for Habitica
|
||||||
// Heavily inspired to Vuex (https://github.com/vuejs/vuex) with a very
|
// Heavily inspired to Vuex (https://github.com/vuejs/vuex) with a very
|
||||||
@@ -22,7 +21,7 @@ const store = {
|
|||||||
// Actions should be called using store.dispatch(ACTION_NAME, ...ARGS)
|
// Actions should be called using store.dispatch(ACTION_NAME, ...ARGS)
|
||||||
// They get passed the store instance and any additional argument passed to dispatch()
|
// They get passed the store instance and any additional argument passed to dispatch()
|
||||||
dispatch (type, ...args) {
|
dispatch (type, ...args) {
|
||||||
let action = get(actions, type);
|
let action = actions[type];
|
||||||
|
|
||||||
if (!action) throw new Error(`Action "${type}" not found.`);
|
if (!action) throw new Error(`Action "${type}" not found.`);
|
||||||
return action(store, ...args);
|
return action(store, ...args);
|
||||||
@@ -60,7 +59,7 @@ export {
|
|||||||
mapState,
|
mapState,
|
||||||
mapGetters,
|
mapGetters,
|
||||||
mapActions,
|
mapActions,
|
||||||
} from './helpers';
|
} from './helpers/public';
|
||||||
|
|
||||||
// Setup internal Vue instance to make state and getters reactive
|
// Setup internal Vue instance to make state and getters reactive
|
||||||
_vm = new Vue({
|
_vm = new Vue({
|
||||||
|
|||||||
Reference in New Issue
Block a user