Created
October 16, 2018 21:12
-
-
Save unr/345fe5c9daf7e5daffa1f23842d2c9ab to your computer and use it in GitHub Desktop.
Bare Minimum Laravel Echo in Nuxt.JS example.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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; |
@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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@unr How do you work around the "do not mutate vuex store state outside mutation handlers." errors?