Skip to content

Instantly share code, notes, and snippets.

@dbismut
Last active January 25, 2022 09:02
Show Gist options
  • Select an option

  • Save dbismut/35b89185998d68bc18fd to your computer and use it in GitHub Desktop.

Select an option

Save dbismut/35b89185998d68bc18fd to your computer and use it in GitHub Desktop.
React Redux Meteor middlewares
// loading user (compatible with react-router-ssr)
export function loadUserLoggingIn() {
return {
type: USER_LOGGING_IN,
meteor: {
get: () => Meteor.loggingIn()
}
};
}
export function loadUserData() {
return {
type: USER_DATA,
meteor: {
subscribe: {
name: 'userData',
get: () => Meteor.users.findOne(Meteor.userId())
}
}
};
}
// loading elements from collection
export function loadPosts() {
return {
type: POSTS,
meteor: {
subscribe: {
name: 'posts',
get: () => Posts.find().fetch()
}
}
}
}
// dispatching Meteor method
export function signIn(email, password) {
return {
type: USER_SIGNIN,
meteor: {
call: {
method: Meteor.loginWithPassword,
params: [email, password]
}
}
}
}
// dispatching Meteor call
export function checkUsername(username) {
return {
type: USER_CHECKUSERNAME,
meteor: {
call: {
method: 'users.usernameIsFree',
params: [{username}]
}
}
}
}
export function actionTypeBuilder(prefix) {
return {
type: actionType => `${prefix}/${actionType}`,
loading: actionType => `${actionType}/loading`,
ready: actionType => `${actionType}/ready`,
stopped: actionType => `${actionType}/stopped`,
changed: actionType => `${actionType}/changed`,
error: actionType => `${actionType}/error`,
success: actionType => `${actionType}/success`
};
}
export default actionTypeBuilder('@myApp');
import actionTypeBuilder from './actionTypeBuilder';
const comps = {};
export default store => next => action => {
if (!action.meteor || (!action.meteor.get && !action.meteor.unsubscribe)) {
return next(action);
}
const { get, unsubscribe } = action.meteor;
if (Meteor.isServer) {
const data = get();
next({ type: actionTypeBuilder.changed(action.type), data });
return Promise.resolve();
}
// If we already have an handle for this action
if (comps[action.type]) {
if (unsubscribe) {
comps[action.type].stop();
}
const data = get();
next({ type: actionTypeBuilder.changed(action.type), data });
return Promise.resolve();
}
return new Promise((resolve, reject) => {
comps[action.type] = Tracker.autorun(() => {
const data = get();
next({ type: actionTypeBuilder.changed(action.type), data });
return resolve();
});
comps[action.type].onStop(() => {
next({ type: actionTypeBuilder.stopped(action.type), name } );
delete comps[action.type];
});
});
};
import actionTypeBuilder from './actionTypeBuilder';
export default store => next => action => {
if (!action.meteor || !action.meteor.call) {
return next(action);
}
const { method, params, showError } = action.meteor.call;
const parameters = params || [];
const meteorMethod = typeof method === 'string' ? Meteor.call : method;
if (typeof method === 'string') {
parameters.unshift(method);
}
return new Promise((resolve, reject) => {
next({ type: actionTypeBuilder.loading(action.type) });
meteorMethod(...parameters, function(error, result) {
if (error) {
next({ type: actionTypeBuilder.error(action.type), error });
if(showError) {
next(showNotification(error));
}
return reject(error);
}
next({ type: actionTypeBuilder.success(action.type) });
return resolve(result);
});
});
};
import actionTypeBuilder from './actionTypeBuilder';
import { SUBSCRIPTIONS } from './subscriptionsActions';
const subs = {};
export default store => next => action => {
if (!action.meteor || (!action.meteor.subscribe && !action.meteor.unsubscribe)) {
return next(action);
}
const { name } = action.meteor.subscribe || action.meteor.unsubscribe;
const sub = subs[name];
if (action.meteor.unsubscribe) {
if (sub) {
delete sub.subscribedActions[action.type];
if (_.isEmpty(sub.subscribedActions)) {
sub.handle.stop();
}
}
return Promise.resolve();
}
const { params, get } = action.meteor.subscribe;
const parameters = params || [];
if (Meteor.isServer && action.meteor.subscribe) {
Meteor.subscribe(name, ...parameters);
next({ type: actionTypeBuilder.ready(SUBSCRIPTIONS), name } );
if (get) {
return next({ type: action.type, meteor : { get } });
}
return Promise.resolve();
}
const existing = sub && _.isEqual(sub.parameters, parameters);
if (existing && sub.handle.ready()) {
if (! sub.subscribedActions[action.type]) {
sub.subscribedActions[action.type] = 'ready';
}
if (get) {
return next({ type: action.type, meteor : { get } });
}
return Promise.resolve();
}
if (sub && sub.handle.ready()) {
sub.handle.stop();
}
return new Promise((resolve, reject) => {
next({ type: actionTypeBuilder.loading(SUBSCRIPTIONS), name });
const handle = Meteor.subscribe(name, ...parameters, {
onReady: () => {
next({ type: actionTypeBuilder.ready(SUBSCRIPTIONS), name } );
if (get) {
return next({ type: action.type, meteor : { get } }).then(resolve);
}
return resolve();
},
onStop: (error) => {
next({ type: actionTypeBuilder.stopped(SUBSCRIPTIONS), name } );
delete subs[name];
if (error) {
next({ type: actionTypeBuilder.error(SUBSCRIPTIONS), name, error } );
reject(error);
}
}
});
subs[name] = {
subscribedActions: {[action.type]: 'ready'},
parameters: _.clone(parameters),
handle: handle
};
});
};
export default function(state = [], action) {
const { type, data } = action;
switch (type) {
case actionTypeBuilder.changed(POSTS):
return data;
default:
return state;
}
}
import actionTypeBuilder from './actionTypeBuilder';
export const SUBSCRIPTIONS = actionTypeBuilder.type('SUBSCRIPTIONS');
export function subscribeTo(name, type, ...params) {
return {
type: type,
meteor: {
subscribe: {
name: name,
params: params
}
}
}
}
import actionTypeBuilder from './actionTypeBuilder';
import { SUBSCRIPTIONS } from './subscriptionsActions';
const initialState = {};
export default function(state = initialState, action) {
const { type, name } = action;
switch (type) {
case actionTypeBuilder.ready(SUBSCRIPTIONS):
return {...state, [name]: {ready: true} };
case actionTypeBuilder.loading(SUBSCRIPTIONS):
return {...state, [name]: {ready: false, loading: true} };
case actionTypeBuilder.stopped(SUBSCRIPTIONS):
return {...state, [name]: {ready: false, loading: false} };
case actionTypeBuilder.error(SUBSCRIPTIONS):
return {...state, [name]: {ready: false, error: true, loading: false} };
default:
return state;
}
}
@michaltakac
Copy link
Copy Markdown

Btw thank you very much for this gist!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment