Skip to content

Commit 2e8977d

Browse files
committed
refactor(sockets): more socket work [unstable]
1 parent 0d29a9a commit 2e8977d

20 files changed

+490
-150
lines changed

package.json

+2
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@
9999
"react-html-parser": "2.0.2",
100100
"react-infinite-scroller": "1.2.5",
101101
"react-redux": "7.2.6",
102+
"react-singleton-hook": "3.4.0",
102103
"redis": "4.0.3",
103104
"redux": "4.1.2",
104105
"redux-actions": "2.6.5",
@@ -166,6 +167,7 @@
166167
"grunt-parallel": "0.5.1",
167168
"grunt-sass": "3.1.0",
168169
"grunt-shell": "3.0.1",
170+
"html-webpack-plugin": "5.5.0",
169171
"husky": "7.0.4",
170172
"lint-staged": "12.3.4",
171173
"lorem-ipsum": "2.0.4",

src/client/actions/common.js

+9
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
import { createAction } from 'redux-actions'
1616
import {
17+
INIT_SOCKET,
18+
UPDATE_SOCKET,
1719
SHOW_MODAL,
1820
HIDE_MODAL,
1921
CLEAR_MODAL,
@@ -24,6 +26,13 @@ import {
2426
CLEAR_NOTICE
2527
} from 'actions/types'
2628

29+
export const initSocket = createAction(
30+
INIT_SOCKET.ACTION,
31+
payload => payload,
32+
() => ({ thunk: true })
33+
)
34+
export const updateSocket = createAction(UPDATE_SOCKET.ACTION, payload => payload)
35+
2736
export const showModal = createAction(SHOW_MODAL.ACTION, (modalType, modalProps) => ({ modalType, modalProps }))
2837
export const hideModal = createAction(HIDE_MODAL.ACTION)
2938
export const clearModal = createAction(CLEAR_MODAL.ACTION)

src/client/actions/types.js

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import { defineAction } from 'redux-define'
1616
import { PENDING, ERROR, SUCCESS } from './stateConstants'
1717

1818
// Shared
19+
export const INIT_SOCKET = defineAction('INIT_SOCKET', [SUCCESS, ERROR])
20+
export const UPDATE_SOCKET = defineAction('UPDATE_SOCKET', [SUCCESS, ERROR])
1921
export const SHOW_MODAL = defineAction('SHOW_MODAL')
2022
export const HIDE_MODAL = defineAction('HIDE_MODAL')
2123
export const CLEAR_MODAL = defineAction('CLEAR_MODAL')

src/client/app.jsx

+7-1
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,14 @@ import createSagaMiddleware from 'redux-saga'
2020
import { middleware as thunkMiddleware } from 'redux-saga-thunk'
2121
import IndexReducer from './reducers'
2222
import IndexSagas from './sagas'
23+
import { SingletonHooksContainer } from 'react-singleton-hook'
2324
import TopbarContainer from './containers/Topbar/TopbarContainer'
2425
import Sidebar from './components/Nav/Sidebar/index.jsx'
2526
import ModalRoot from './containers/Modals'
2627
import renderer from './renderer'
2728

2829
import $ from 'jquery'
30+
import SocketGlobal from 'containers/Global/SocketGlobal'
2931

3032
const sagaMiddleware = createSagaMiddleware()
3133

@@ -68,7 +70,11 @@ if (document.getElementById('modal-wrapper')) {
6870
if (document.getElementById('topbar')) {
6971
const TopbarRoot = (
7072
<Provider store={store}>
71-
<TopbarContainer />
73+
<>
74+
<SingletonHooksContainer />
75+
<SocketGlobal />
76+
<TopbarContainer />
77+
</>
7278
</Provider>
7379
)
7480

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import React from 'react'
2+
import PropTypes from 'prop-types'
3+
import { connect } from 'react-redux'
4+
import { initSocket, updateSocket } from 'actions/common'
5+
import helpers from 'lib/helpers'
6+
import TicketSocketEvents from 'lib2/socket/ticketSocketEvents'
7+
8+
class SocketGlobal extends React.Component {
9+
constructor (props) {
10+
super(props)
11+
12+
this.onSocketInitialized = this.onSocketInitialized.bind(this)
13+
this.refreshSocketState = this.refreshSocketState.bind(this)
14+
this.onReconnect = this.onReconnect.bind(this)
15+
this.onDisconnect = this.onDisconnect.bind(this)
16+
17+
this.props.initSocket().then(this.onSocketInitialized)
18+
}
19+
20+
componentWillUnmount () {
21+
if (this.props.socket) {
22+
this.props.socket.off('connect', this.refreshSocketState)
23+
this.props.socket.off('connecting', this.refreshSocketState)
24+
this.props.socket.io.off('reconnect', this.onReconnect)
25+
this.props.socket.off('disconnect', this.onDisconnect)
26+
}
27+
}
28+
29+
onSocketInitialized () {
30+
this.props.socket.on('connect', this.onReconnect)
31+
this.props.socket.on('connecting', this.refreshSocketState)
32+
this.props.socket.io.on('reconnect', this.onReconnect)
33+
this.props.socket.on('disconnect', this.onDisconnect)
34+
35+
// Load any initial socket stuff
36+
}
37+
38+
onDisconnect (socket) {
39+
helpers.UI.showDisconnectedOverlay()
40+
this.refreshSocketState({ socket })
41+
42+
const self = this
43+
this.props.socket.io.removeAllListeners('reconnect_attempt')
44+
this.props.socket.io.on('reconnect_attempt', function (s) {
45+
helpers.UI.showDisconnectedOverlay()
46+
self.refreshSocketState({ socket: s })
47+
})
48+
49+
this.props.socket.removeAllListeners('connect_timeout')
50+
this.props.socket.on('connect_timeout', function (s) {
51+
helpers.UI.showDisconnectedOverlay()
52+
self.refreshSocketState({ socket: s })
53+
})
54+
}
55+
56+
onReconnect (socket) {
57+
helpers.UI.hideDisconnectedOverlay()
58+
this.props.updateSocket({ socket })
59+
}
60+
61+
refreshSocketState (socket) {
62+
this.props.updateSocket({ socket })
63+
}
64+
65+
render () {
66+
return (
67+
<>
68+
<TicketSocketEvents />
69+
</>
70+
)
71+
}
72+
}
73+
74+
SocketGlobal.propTypes = {
75+
initSocket: PropTypes.func.isRequired,
76+
updateSocket: PropTypes.func.isRequired,
77+
socket: PropTypes.object.isRequired
78+
}
79+
80+
const mapStateToProps = state => ({
81+
socket: state.shared.socket
82+
})
83+
84+
export default connect(mapStateToProps, { initSocket, updateSocket })(SocketGlobal)

src/client/containers/Tickets/SingleTicketContainer.jsx

+39-19
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { observer } from 'mobx-react'
1919
import sortBy from 'lodash/sortBy'
2020
import union from 'lodash/union'
2121

22-
import { transferToThirdParty } from 'actions/tickets'
22+
import { transferToThirdParty, fetchTicketTypes } from 'actions/tickets'
2323
import { fetchGroups, unloadGroups } from 'actions/groups'
2424
import { showModal } from 'actions/common'
2525

@@ -101,6 +101,7 @@ class SingleTicketContainer extends React.Component {
101101
socket.socket.on('updateTicketTags', this.onUpdateTicketTags)
102102

103103
fetchTicket(this)
104+
this.props.fetchTicketTypes()
104105
this.props.fetchGroups()
105106
}
106107

@@ -243,9 +244,9 @@ class SingleTicketContainer extends React.Component {
243244
})
244245
: []
245246

246-
const mappedTypes = this.props.common.ticketTypes
247-
? this.props.common.ticketTypes.map(type => {
248-
return { text: type.name, value: type._id, raw: type }
247+
const mappedTypes = this.props.ticketTypes
248+
? this.props.ticketTypes.map(type => {
249+
return { text: type.get('name'), value: type.get('_id'), raw: type.toJS() }
249250
})
250251
: []
251252

@@ -341,14 +342,18 @@ class SingleTicketContainer extends React.Component {
341342
<select
342343
value={this.ticket.type._id}
343344
onChange={e => {
344-
const type = this.props.common.ticketTypes.find(t => t._id === e.target.value)
345-
const hasPriority =
346-
type.priorities.findIndex(p => p._id === this.ticket.priority._id) !== -1
345+
const type = this.props.ticketTypes.find(t => t.get('_id') === e.target.value)
346+
347+
const priority = type
348+
.get('priorities')
349+
.findIndex(p => p.get('_id') === this.ticket.priority._id)
350+
351+
const hasPriority = priority !== -1
347352

348353
if (!hasPriority) {
349354
socket.ui.setTicketPriority(
350355
this.ticket._id,
351-
type.priorities.find(() => true)
356+
type.get('priorities').find(() => true)
352357
)
353358
showPriorityConfirm()
354359
}
@@ -438,7 +443,7 @@ class SingleTicketContainer extends React.Component {
438443
)}
439444
{!hasTicketUpdate && (
440445
<div className='input-box'>
441-
{helpers.formatDate(this.ticket.dueDate, this.props.common.shortDateFormat)}
446+
{helpers.formatDate(this.ticket.dueDate, this.props.common.get('shortDateFormat'))}
442447
</div>
443448
)}
444449
</div>
@@ -489,7 +494,9 @@ class SingleTicketContainer extends React.Component {
489494
{this.ticket.history &&
490495
this.ticket.history.map(item => (
491496
<div key={item._id} className='history-item'>
492-
<time dateTime={helpers.formatDate(item.date, this.props.common.longDateFormat)} />
497+
<time
498+
dateTime={helpers.formatDate(item.date, this.props.common.get('longDateFormat'))}
499+
/>
493500
<em>
494501
Action by: <span>{item.owner.fullname}</span>
495502
</em>
@@ -506,7 +513,7 @@ class SingleTicketContainer extends React.Component {
506513
{/* Right Side */}
507514
<div className='page-message nopadding' style={{ marginLeft: 360 }}>
508515
<div className='page-title-right noshadow'>
509-
{this.props.common.hasThirdParty && (
516+
{this.props.common.get('hasThirdParty') && (
510517
<div className='page-top-comments uk-float-right'>
511518
<a
512519
role='button'
@@ -576,7 +583,7 @@ class SingleTicketContainer extends React.Component {
576583
subject={this.ticket.subject}
577584
issue={this.ticket.issue}
578585
date={this.ticket.date}
579-
dateFormat={`${this.props.common.longDateFormat}, ${this.props.common.timeFormat}`}
586+
dateFormat={`${this.props.common.get('longDateFormat')}, ${this.props.common.get('timeFormat')}`}
580587
attachments={this.ticket.attachments}
581588
editorWindow={this.editorWindow}
582589
/>
@@ -618,7 +625,9 @@ class SingleTicketContainer extends React.Component {
618625
ticketSubject={this.ticket.subject}
619626
comment={item}
620627
isNote={item.isNote}
621-
dateFormat={`${this.props.common.longDateFormat}, ${this.props.common.timeFormat}`}
628+
dateFormat={`${this.props.common.get('longDateFormat')}, ${this.props.common.get(
629+
'timeFormat'
630+
)}`}
622631
onEditClick={() => {
623632
this.editorWindow.openEditorWindow({
624633
showSubject: false,
@@ -646,7 +655,9 @@ class SingleTicketContainer extends React.Component {
646655
ticketStatus={this.ticket.status}
647656
ticketSubject={this.ticket.subject}
648657
comment={comment}
649-
dateFormat={`${this.props.common.longDateFormat}, ${this.props.common.timeFormat}`}
658+
dateFormat={`${this.props.common.get('longDateFormat')}, ${this.props.common.get(
659+
'timeFormat'
660+
)}`}
650661
onEditClick={() => {
651662
this.editorWindow.openEditorWindow({
652663
showSubject: false,
@@ -673,7 +684,9 @@ class SingleTicketContainer extends React.Component {
673684
ticketSubject={this.ticket.subject}
674685
comment={note}
675686
isNote={true}
676-
dateFormat={`${this.props.common.longDateFormat}, ${this.props.common.timeFormat}`}
687+
dateFormat={`${this.props.common.get('longDateFormat')}, ${this.props.common.get(
688+
'timeFormat'
689+
)}`}
677690
onEditClick={() => {
678691
this.editorWindow.openEditorWindow({
679692
showSubject: false,
@@ -781,6 +794,8 @@ SingleTicketContainer.propTypes = {
781794
ticketUid: PropTypes.string.isRequired,
782795
shared: PropTypes.object.isRequired,
783796
common: PropTypes.object.isRequired,
797+
ticketTypes: PropTypes.object.isRequired,
798+
fetchTicketTypes: PropTypes.func.isRequired,
784799
groupsState: PropTypes.object.isRequired,
785800
fetchGroups: PropTypes.func.isRequired,
786801
unloadGroups: PropTypes.func.isRequired,
@@ -789,11 +804,16 @@ SingleTicketContainer.propTypes = {
789804
}
790805

791806
const mapStateToProps = state => ({
792-
common: state.common,
807+
common: state.common.viewdata,
793808
shared: state.shared,
809+
ticketTypes: state.ticketsState.types,
794810
groupsState: state.groupsState
795811
})
796812

797-
export default connect(mapStateToProps, { fetchGroups, unloadGroups, showModal, transferToThirdParty })(
798-
SingleTicketContainer
799-
)
813+
export default connect(mapStateToProps, {
814+
fetchTicketTypes,
815+
fetchGroups,
816+
unloadGroups,
817+
showModal,
818+
transferToThirdParty
819+
})(SingleTicketContainer)

src/client/containers/Topbar/TopbarContainer.jsx

+16-10
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import OnlineUserListPartial from 'containers/Topbar/onlineUserList'
3939

4040
import helpers from 'lib/helpers'
4141
import Cookies from 'jscookie'
42+
import { NOTIFICATIONS_UPDATE, USERS_UPDATE } from 'serverSocket/socketEventConsts'
4243

4344
@observer
4445
class TopbarContainer extends React.Component {
@@ -63,23 +64,26 @@ class TopbarContainer extends React.Component {
6364
this.showNotice(this.props.viewdata.get('notice'), this.props.viewdata.get('noticeCookieName'))
6465
})
6566

66-
socket.socket.on('updateNotifications', this.onSocketUpdateNotifications)
67-
socket.socket.on('updateUsers', this.onSocketUpdateUsers)
68-
socket.socket.on('$trudesk:notice:show', this.onSocketShowNotice)
69-
socket.socket.on('updateClearNotice', this.onSocketClearNotice)
67+
this.props.socket.on(NOTIFICATIONS_UPDATE, this.onSocketUpdateNotifications)
68+
this.props.socket.on(USERS_UPDATE, this.onSocketUpdateUsers)
69+
this.props.socket.on('$trudesk:notice:show', this.onSocketShowNotice)
70+
this.props.socket.on('updateClearNotice', this.onSocketClearNotice)
7071

7172
// Call for an update on Mount
72-
socket.ui.updateNotifications()
73-
socket.ui.updateUsers()
73+
this.props.socket.emit(NOTIFICATIONS_UPDATE)
74+
this.props.socket.emit(USERS_UPDATE)
75+
76+
// socket.ui.updateNotifications()
77+
// socket.ui.updateUsers()
7478

7579
// this.shouldShowBanner()
7680
}
7781

7882
componentWillUnmount () {
79-
socket.socket.off('updateNotifications', this.onSocketUpdateNotifications)
80-
socket.socket.off('updateUsers', this.onSocketUpdateUsers)
81-
socket.socket.off('$trudesk:notice:show', this.onSocketShowNotice)
82-
socket.socket.off('updateClearNotice', this.onSocketClearNotice)
83+
this.props.socket.off(NOTIFICATIONS_UPDATE, this.onSocketUpdateNotifications)
84+
this.props.socket.off(USERS_UPDATE, this.onSocketUpdateUsers)
85+
this.props.socket.off('$trudesk:notice:show', this.onSocketShowNotice)
86+
this.props.socket.off('updateClearNotice', this.onSocketClearNotice)
8387
}
8488

8589
shouldShowBanner () {
@@ -263,6 +267,7 @@ class TopbarContainer extends React.Component {
263267
}
264268

265269
TopbarContainer.propTypes = {
270+
socket: PropTypes.object.isRequired,
266271
sessionUser: PropTypes.object,
267272
fetchViewData: PropTypes.func.isRequired,
268273
loadingViewData: PropTypes.bool.isRequired,
@@ -275,6 +280,7 @@ TopbarContainer.propTypes = {
275280
}
276281

277282
const mapStateToProps = state => ({
283+
socket: state.shared.socket,
278284
sessionUser: state.shared.sessionUser,
279285
notice: state.shared.notice,
280286
loadingViewData: state.common.loadingViewData,

0 commit comments

Comments
 (0)