Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 64f285f

Browse files
implement real time client
1 parent c0baf13 commit 64f285f

27 files changed

+4251
-3758
lines changed

‎front-end/package-lock.json

Lines changed: 3729 additions & 3742 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎front-end/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"noty": "^3.2.0-beta",
2424
"npm": "^6.4.0",
2525
"popper.js": "^1.14.4",
26+
"sockjs-client": "^1.1.5",
2627
"vue": "^2.5.17",
2728
"vue-i18n": "^8.0.0",
2829
"vue-router": "^3.0.1",

‎front-end/src/App.vue

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,13 @@
88
import 'bootstrap/dist/js/bootstrap.min'
99
1010
export default {
11-
name: 'App'
11+
name: 'App',
12+
created () {
13+
this.$bus.$on('myDataFetched', myData => {
14+
// Initializing the real time connection
15+
this.$rt.init(myData.settings.realTimeServerUrl, myData.user.token)
16+
})
17+
}
1218
}
1319
</script>
1420

‎front-end/src/components/PageHeader.vue

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,10 @@ export default {
6060
'teamBoards'
6161
])
6262
},
63-
created () {
64-
this.$store.dispatch('getMyData')
63+
mounted () {
64+
if (!this.user.authenticated) {
65+
this.$store.dispatch('getMyData')
66+
}
6567
},
6668
methods: {
6769
goHome () {
@@ -71,7 +73,10 @@ export default {
7173
this.$router.push({name: 'board', params: { boardId: board.id }})
7274
},
7375
signOut () {
76+
this.$rt.logout()
77+
7478
meService.signOut().then(() => {
79+
this.$store.dispatch('logout')
7580
this.$router.push({name: 'login'})
7681
}).catch(error => {
7782
notify.error(error.message)

‎front-end/src/event-bus.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import Vue from 'vue'
2+
3+
const bus = new Vue({})
4+
5+
export default bus

‎front-end/src/main.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import { library as faLibrary } from '@fortawesome/fontawesome-svg-core'
88
import { faHome, faSearch, faPlus, faEllipsisH, faUserPlus, faListUl } from '@fortawesome/free-solid-svg-icons'
99
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
1010
import { i18n } from './i18n'
11+
import eventBus from './event-bus'
12+
import realTimeClient from '@/real-time-client'
1113

1214
// Bootstrap axios
1315
axios.defaults.baseURL = '/api'
@@ -28,6 +30,9 @@ Vue.component('font-awesome-icon', FontAwesomeIcon)
2830

2931
Vue.config.productionTip = false
3032

33+
Vue.prototype.$bus = eventBus
34+
Vue.prototype.$rt = realTimeClient
35+
3136
new Vue({
3237
router,
3338
store,

‎front-end/src/real-time-client.js

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
import Vue from 'vue'
2+
import SockJS from 'sockjs-client'
3+
import globalBus from '@/event-bus'
4+
5+
class RealTimeClient {
6+
constructor () {
7+
this.serverUrl = null
8+
this.token = null
9+
this.socket = null
10+
// If the client is authenticated through real time connection or not
11+
this.authenticated = false
12+
this.loggedOut = false
13+
this.$bus = new Vue()
14+
this.subscribeQueue = {
15+
/* channel: [handler1, handler2] */
16+
}
17+
this.unsubscribeQueue = {
18+
/* channel: [handler1, handler2] */
19+
}
20+
}
21+
init (serverUrl, token) {
22+
if (this.authenticated) {
23+
console.warn('[RealTimeClient] WS connection already authenticated.')
24+
return
25+
}
26+
console.log('[RealTimeClient] Initializing')
27+
this.serverUrl = serverUrl
28+
this.token = token
29+
this.connect()
30+
}
31+
logout () {
32+
console.log('[RealTimeClient] Logging out')
33+
this.subscribeQueue = {}
34+
this.unsubscribeQueue = {}
35+
this.authenticated = false
36+
this.loggedOut = true
37+
this.socket && this.socket.close()
38+
}
39+
connect () {
40+
console.log('[RealTimeClient] Connecting to ' + this.serverUrl)
41+
this.socket = new SockJS(this.serverUrl + '?token=' + this.token)
42+
this.socket.onopen = () => {
43+
// Once the connection established, always set the client as authenticated
44+
this.authenticated = true
45+
this._onConnected()
46+
}
47+
this.socket.onmessage = (event) => {
48+
this._onMessageReceived(event)
49+
}
50+
this.socket.onerror = (error) => {
51+
this._onSocketError(error)
52+
}
53+
this.socket.onclose = (event) => {
54+
this._onClosed(event)
55+
}
56+
}
57+
subscribe (channel, handler) {
58+
if (!this._isConnected()) {
59+
this._addToSubscribeQueue(channel, handler)
60+
return
61+
}
62+
const message = {
63+
action: 'subscribe',
64+
channel
65+
}
66+
this._send(message)
67+
this.$bus.$on(this._channelEvent(channel), handler)
68+
console.log('[RealTimeClient] Subscribed to channel ' + channel)
69+
}
70+
unsubscribe (channel, handler) {
71+
// Already logged out, no need to unsubscribe
72+
if (this.loggedOut) {
73+
return
74+
}
75+
76+
if (!this._isConnected()) {
77+
this._addToUnsubscribeQueue(channel, handler)
78+
return
79+
}
80+
const message = {
81+
action: 'unsubscribe',
82+
channel
83+
}
84+
this._send(message)
85+
this.$bus.$off(this._channelEvent(channel), handler)
86+
console.log('[RealTimeClient] Unsubscribed from channel ' + channel)
87+
}
88+
_isConnected () {
89+
return this.socket && this.socket.readyState === SockJS.OPEN
90+
}
91+
_onConnected () {
92+
globalBus.$emit('RealTimeClient.connected')
93+
console.log('[RealTimeClient] Connected')
94+
95+
// Handle subscribe and unsubscribe queue
96+
this._processQueues()
97+
}
98+
_onMessageReceived (event) {
99+
const message = JSON.parse(event.data)
100+
console.log('[RealTimeClient] Received message', message)
101+
102+
if (message.channel) {
103+
this.$bus.$emit(this._channelEvent(message.channel), message.payload)
104+
}
105+
}
106+
_send (message) {
107+
this.socket.send(JSON.stringify(message))
108+
}
109+
_onSocketError (error) {
110+
console.error('[RealTimeClient] Socket error', error)
111+
}
112+
_onClosed (event) {
113+
console.log('[RealTimeClient] Received close event', event)
114+
if (this.loggedOut) {
115+
// Manually logged out, no need to reconnect
116+
console.log('[RealTimeClient] Logged out')
117+
globalBus.$emit('RealTimeClient.loggedOut')
118+
} else {
119+
// Temporarily disconnected, attempt reconnect
120+
console.log('[RealTimeClient] Disconnected')
121+
globalBus.$emit('RealTimeClient.disconnected')
122+
123+
setTimeout(() => {
124+
console.log('[RealTimeClient] Reconnecting')
125+
globalBus.$emit('RealTimeClient.reconnecting')
126+
this.connect()
127+
}, 1000)
128+
}
129+
}
130+
_channelEvent (channel) {
131+
return 'channel:' + channel
132+
}
133+
_processQueues () {
134+
console.log('[RealTimeClient] Processing subscribe/unsubscribe queues')
135+
136+
// Process subscribe queue
137+
const subscribeChannels = Object.keys(this.subscribeQueue)
138+
subscribeChannels.forEach(channel => {
139+
const handlers = this.subscribeQueue[channel]
140+
handlers.forEach(handler => {
141+
this.subscribe(channel, handler)
142+
this._removeFromQueue(this.subscribeQueue, channel, handler)
143+
})
144+
})
145+
146+
// Process unsubscribe queue
147+
const unsubscribeChannels = Object.keys(this.unsubscribeQueue)
148+
unsubscribeChannels.forEach(channel => {
149+
const handlers = this.unsubscribeQueue[channel]
150+
handlers.forEach(handler => {
151+
this.unsubscribe(channel, handler)
152+
this._removeFromQueue(this.unsubscribeQueue, channel, handler)
153+
})
154+
})
155+
}
156+
_addToSubscribeQueue (channel, handler) {
157+
console.log('[RealTimeClient] Adding channel subscribe to queue. Channel: ' + channel)
158+
// To make sure the unsubscribe won't be sent out to the server
159+
this._removeFromQueue(this.unsubscribeQueue, channel, handler)
160+
const handlers = this.subscribeQueue[channel]
161+
if (!handlers) {
162+
this.subscribeQueue[channel] = [handler]
163+
} else {
164+
handlers.push(handler)
165+
}
166+
}
167+
_addToUnsubscribeQueue (channel, handler) {
168+
console.log('[RealTimeClient] Adding channel unsubscribe to queue. Channel: ' + channel)
169+
// To make sure the subscribe won't be sent out to the server
170+
this._removeFromQueue(this.subscribeQueue, channel, handler)
171+
const handlers = this.unsubscribeQueue[channel]
172+
if (!handlers) {
173+
this.unsubscribeQueue[channel] = [handler]
174+
} else {
175+
handlers.push(handlers)
176+
}
177+
}
178+
_removeFromQueue (queue, channel, handler) {
179+
const handlers = queue[channel]
180+
if (handlers) {
181+
let index = handlers.indexOf(handler)
182+
if (index > -1) {
183+
handlers.splice(index, 1)
184+
}
185+
}
186+
}
187+
}
188+
189+
export default new RealTimeClient()

‎front-end/src/services/me.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import axios from 'axios'
22
import errorParser from '@/utils/error-parser'
3+
import eventBus from '@/event-bus'
34

45
export default {
56
/**
@@ -9,6 +10,7 @@ export default {
910
return new Promise((resolve, reject) => {
1011
axios.get('/me').then(({data}) => {
1112
resolve(data)
13+
eventBus.$emit('myDataFetched', data)
1214
}).catch((error) => {
1315
reject(errorParser.parse(error))
1416
})

‎front-end/src/store/actions.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import meService from '@/services/me'
22

3+
export const logout = ({ commit }) => {
4+
commit('logout')
5+
}
6+
37
export const getMyData = ({ commit }) => {
48
meService.getMyData().then(data => {
59
commit('updateMyData', data)

‎front-end/src/store/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ Vue.use(Vuex)
99

1010
const state = {
1111
user: {
12-
name: null
12+
name: null,
13+
authenticated: false
1314
},
1415
teams: [/* {id, name} */],
1516
boards: [/* {id, name, description, teamId} */]

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /