-
-
Save unr/345fe5c9daf7e5daffa1f23842d2c9ab to your computer and use it in GitHub Desktop.
// In my global app layout, once my app is mounted and ready to listen... | |
import { mapActions, mapGetters } from 'vuex'; | |
const Layout = { | |
mounted() { | |
// set up pusher listeners | |
this.connectToPublicChannel(); | |
this.$watch('authenticated', (auth) => { | |
if (auth) this.connectToUserChannel(); | |
else this.leaveUserChannel(); | |
}, { immediate: true }); | |
// maintains window width, for use app-wide | |
this.updateWindowWidth(window.innerWidth); | |
window.addEventListener('resize', throttle(() => { | |
this.updateWindowWidth(window.innerWidth); | |
}), 400); | |
}, | |
beforeDestroy() { | |
// leaves channels, so next layout will mount fresh channels. | |
this.leaveAllChannels(); | |
}, | |
computed: { | |
...mapGetters({ | |
authenticated: 'user/authenticated', | |
}), | |
}, | |
methods: { | |
...mapActions({ | |
connectToPublicChannel: 'connectToPublicChannel', | |
connectToUserChannel: 'connectToUserChannel', | |
leaveAllChannels: 'leaveAllChannels', | |
leaveUserChannel: 'leaveUserChannel', | |
}), | |
}, | |
}; | |
export default Layout; |
/** | |
* Echo Module | |
* Sets up listeners for pusher channels, and delegates their responses. | |
*/ | |
import Vue from 'vue'; | |
// eslint-disable-next-line | |
import Pusher from 'pusher-js'; | |
import Echo from 'laravel-echo'; | |
const EchoModule = { | |
state() { | |
return { | |
public: false, | |
private: false, | |
}; | |
}, | |
mutations: { | |
setPublic(state, value) { | |
Vue.set(state, 'public', value); | |
}, | |
setPrivate(state, value) { | |
Vue.set(state, 'private', value); | |
}, | |
}, | |
actions: { | |
// Creates a public echo instance, for casual updates | |
createPublic({ commit, state }) { | |
return new Promise((resolve, reject) => { | |
if (state.public) return resolve(true); | |
this.$axios.$get('refresh-csrf').then(() => { | |
const instance = new Echo({ | |
broadcaster: 'pusher', | |
key: process.env.pusherAppKey, | |
cluster: process.env.pusherCluster, | |
encrypted: true, | |
}); | |
commit('setPublic', instance); | |
return resolve(true); | |
}) | |
.catch(err => reject(err)); | |
}); | |
}, | |
// Creates a private cookie-based instance, for user-updates | |
createPrivate({ commit, state }) { | |
return new Promise((resolve, reject) => { | |
if (state.private) return resolve(true); | |
const cookie = Cookie.parse(document.cookie); | |
this.$axios.$get('refresh-csrf').then(() => { | |
const instance = new Echo({ | |
broadcaster: 'pusher', | |
key: process.env.pusherAppKey, | |
cluster: process.env.pusherCluster, | |
encrypted: true, | |
auth: { | |
// Echo is NOT using app.$axios -- so it needs to read the XSRF TOKEN itself | |
headers: { 'X-XSRF-TOKEN': cookie['XSRF-TOKEN'] }, | |
}, | |
}); | |
commit('setPrivate', instance); | |
return resolve(true); | |
}) | |
.catch(err => reject(err)); | |
}); | |
}, | |
// Connects the current user to the public echo channel | |
connectToPublicChannel({ dispatch, state }) { | |
dispatch('createPublic').then(() => { | |
// handle public match updates | |
state.public.channel('matches') | |
.listen('BettingStatusChanged', notification => dispatch('handleBettingStatusChanged', notification)) | |
.listen('MatchOutcomeOddsChanged', notification => dispatch('handleMatchOutcomeOddsChanged', notification)) | |
.listen('MatchScheduledAtChanged', notification => dispatch('handleMatchScheduledAtChanged', notification)) | |
.listen('MatchMarketsStatusChanged', notification => dispatch('handleMatchMarketsStatusChanged', notification)) | |
.listen('MatchStatusChanged', notification => dispatch('handleMatchStatusChanged', notification)); | |
}) | |
.catch((err) => { | |
if (process.client) { | |
this.$raven.captureMessage('failed to connect to matches channel', { | |
extra: { state, error: err }, | |
}); | |
} | |
}); | |
}, | |
// Connects the current user to the Rivalry Live echo channel | |
connectToLiveChannel({ dispatch, state }) { | |
dispatch('createPublic').then(() => { | |
// handle live match score updates | |
state.public.channel('live') | |
.listen('MatchScoreChanged', notification => dispatch('handleMatchScoreChanged', notification)); | |
}) | |
.catch((err) => { | |
if (process.client) { | |
this.$raven.captureMessage('failed to connect to Live channel', { | |
extra: { state, error: err }, | |
}); | |
} | |
}); | |
}, | |
// Connects the current user to the private echo channel | |
connectToUserChannel({ dispatch, getters, state }) { | |
const uuid = getters['user/uuid']; | |
dispatch('createPrivate').then(() => { | |
state.private.private(`User.${uuid}`) | |
.notification((notification) => { | |
// User's balance was updated | |
if (notification.type.includes('BalanceUpdateNotification')) dispatch('handleBalanceUpdateNotification', notification); | |
}); | |
}) | |
.catch((err) => { | |
if (process.client) { | |
this.$raven.captureMessage('failed to connect to user channel', { | |
extra: { state, uuid, error: err }, | |
}); | |
} | |
}); | |
}, | |
// Leave all channels currently active. | |
// When a Layout is being unmounted, user will leave all channels. | |
// When layout is rendered again, they will join appropriate channels. | |
leaveAllChannels({ state }) { | |
if (state.public) { | |
const channels = Object.keys(state.public.connector.channels); | |
each(channels, channel => state.public.leave(channel)); | |
} | |
if (state.private) { | |
const channels = Object.keys(state.private.connector.channels); | |
each(channels, channel => state.private.leave(channel)); | |
} | |
}, | |
// Leave User Channel | |
// If our user logs out, we need to leave the currently active User channel. | |
leaveUserChannel({ state }) { | |
if (!state.private) return; | |
const channels = Object.keys(state.private.connector.channels); | |
// look for the private.User channel, and leave it if it exists | |
const userChannel = find(channels, channel => channel.includes('private-User')); | |
state.private.leave(userChannel); | |
}, | |
// Leave Live Channel when user leaves the Rivalry Live page | |
leaveLiveChannel({ state }) { | |
if (!state.public) return; | |
const channels = Object.keys(state.public.connector.channels); | |
// look for the live channel, and leave it if it exists | |
const liveChannel = find(channels, ['live']); | |
state.public.leave(liveChannel); | |
}, | |
// User balance updated | |
handleBalanceUpdateNotification({ dispatch }, notification) { | |
dispatch('user/setBalance', notification.balance); | |
}, | |
handleMatchScoreChanged({ dispatch }, notification) { | |
dispatch('matches/updateMatchScores', notification); | |
}, | |
}, | |
}; | |
export default EchoModule; |
Also worth noting - now that I've split my Nuxt.js app from my Laravel API (separate domains), all the Echo.js magic seems lost.
Going to basically write this exact same thing, but mimicing https://github.com/jaggy/vue-pusher/blob/master/index.js instead.
No real benefit of echo
from laravel, when it's not hosted by laravel anymore.
@unr How do you work around the "do not mutate vuex store state outside mutation handlers." errors?
@unr How do you work around the "do not mutate vuex store state outside mutation handlers." errors?
you should try to Object.assign({}, ...)
it
I haven't had those errors while working with something like this. This shouldn't be changing state directly.
The only state manipulation here should be done in mutations fine. Maybe something I just didn't have in my setup?
not working
Food for thought... Thanks...