dashboard/plugins/steve/index.js

168 lines
4.4 KiB
JavaScript

import Vue from 'vue';
import { isArray } from '@/utils/array';
import actions from './actions';
import getters from './getters';
import mutations from './mutations';
import { mutations as subscribeMutations, actions as subscribeActions } from './subscribe';
import { proxyFor } from './resource-proxy';
import { keyFieldFor } from './normalize';
function SteveFactory(namespace, baseUrl) {
return {
strict: false,
namespaced: true,
state() {
return {
config: {
baseUrl,
namespace
},
types: {},
socket: null,
wantSocket: false,
pendingSends: [],
started: [],
noWatch: [],
};
},
getters,
mutations: {
...mutations,
...subscribeMutations,
},
actions: {
...actions,
...subscribeActions
},
};
}
export default (config = {}) => {
const namespace = config.namespace || '';
config.baseUrl = config.baseUrl || `/${ namespace }`;
return function(store) {
const inst = SteveFactory(namespace, config.baseUrl);
store.registerModule(namespace, inst);
store.commit(`${ namespace }/applyConfig`, config);
if ( !process.client || !window.__NUXT__ ) {
return;
}
// store.subscribe(({ type }, state) => {
// if ( type === 'auth/loggedOut' ) {
// store.dispatch(`${ namespace }/unsubscribe`);
// }
// });
const module = store._modules.root._children[namespace];
const fromServer = window.__NUXT__;
const ctx = new Proxy(module.context, {
get(obj, key) {
if ( key === 'rootGetters' ) {
return store.getters;
}
return obj[key];
}
});
// Turn all the objects in the store from the server into proxies
const state = fromServer?.state?.[namespace];
if ( state ) {
Object.keys(state.types).forEach((type) => {
const keyField = keyFieldFor(type);
const cache = state.types[type];
const map = new Map();
for ( let i = 0 ; i < cache.list.length ; i++ ) {
const proxy = proxyFor(ctx, cache.list[i]);
cache.list[i] = proxy;
map.set(proxy[keyField], proxy);
}
Vue.set(cache, 'map', map);
Vue.set(state.types, type, state.types[type]);
});
}
// Turn all the objects in data from the server into the object from the store;
if ( state && fromServer?.data ) {
fromServer.data = recurse(fromServer.data);
}
function recurse(obj, parent, key) {
if ( isArray(obj) ) {
const rehydrateKey = `__rehydrateAll__${ key }`;
if ( parent && key && parent[rehydrateKey] ) {
const [ns, type] = parent[rehydrateKey].split('/', 2);
if ( ns === namespace ) {
// Don't delete the key, so that all the stores go through this path,
// and then do nothing if they are not for this namespace,
// instead of doing the else obj.map()
// and breaking the "live" reference to the cache.list array
// delete parent[rehydrateKey];
const cache = state.types[type];
if ( cache ) {
return cache.list;
}
}
} else {
return obj.map(x => recurse(x));
}
} else if ( obj && typeof obj === 'object' ) {
if ( obj.__rehydrate ) {
if ( obj.__rehydrate !== namespace ) {
// Ignore types that are for another vuex namespace
return obj;
}
const type = obj.type;
const cache = state.types[type];
if ( cache && !obj.__clone ) {
const map = cache.map;
const keyField = keyFieldFor(type);
const entry = map.get(obj[keyField]);
// Map the object to the same instance in the store if possible
if ( entry ) {
return entry;
}
}
// Or just return a proxied object
delete obj.__rehydrate;
return proxyFor(ctx, obj);
} else {
for ( const k of Object.keys(obj) ) {
if ( k.startsWith('__rehydrateAll__') ) {
continue;
}
if ( isArray(obj[k]) || typeof obj[k] === 'object' ) {
obj[k] = recurse(obj[k], obj, k);
}
}
}
}
return obj;
}
};
};