1. process socket event: connect and doc

Signed-off-by: Raccoon <raccoon@hackmd.io>
This commit is contained in:
Raccoon 2020-04-16 05:49:51 +08:00
parent 8ab97d0e0e
commit b804b6b6b6
No known key found for this signature in database
GPG Key ID: 06770355DC9ECD38
1 changed files with 366 additions and 241 deletions

View File

@ -206,10 +206,12 @@ const fileTypes = {
*/ */
// --------------------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------------------
let applicationState = null
let editorInstance = null let editorInstance = null
let editor = null let editor = null
function getEditorInstance () { return editorInstance} function getEditorInstance () { return editorInstance }
function setEditorInstance (e) { function setEditorInstance (e) {
editorInstance = e editorInstance = e
editor = editorInstance.editor editor = editorInstance.editor
@ -224,6 +226,13 @@ function getEditor () {
return null return null
} }
/**
* @returns ApplicationState
*/
function getApplicationState () {
return applicationState
}
// --------------------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------------------
/** /**
@ -233,9 +242,9 @@ function getEditor () {
$(document).ready(function () { $(document).ready(function () {
console.log('-- first phase --') applicationState = new ApplicationState()
initCodeMirrorEditor() initCodeMirrorEditor()
registerCodeMirrorEvent() // registerCodeMirrorEvent()
registerSocketIO() registerSocketIO()
// var inlineAttach = inlineAttachment.editors.codemirror4.attach(editor) // var inlineAttach = inlineAttachment.editors.codemirror4.attach(editor)
defaultTextHeight = parseInt($('.CodeMirror').css('line-height')) defaultTextHeight = parseInt($('.CodeMirror').css('line-height'))
@ -308,7 +317,7 @@ $(document).ready(function () {
}) })
}) })
function initCodeMirrorEditor() { function initCodeMirrorEditor () {
const textit = document.getElementById('textit') const textit = document.getElementById('textit')
if (!textit) { if (!textit) {
throw new Error('There was no textit area!') throw new Error('There was no textit area!')
@ -446,132 +455,54 @@ function registerCodeMirrorEvent () {
}) })
} }
function registerSocketIO () { class ApplicationState {
const realtimeClient = new RealtimeClient() constructor () {
socket = realtimeClient.socket this.connectionStatus = statusType.connected
socket.on('info', function (data) {
console.error(data)
switch (data.code) {
case 403:
location.href = serverurl + '/403'
break
case 404:
location.href = serverurl + '/404'
break
case 500:
location.href = serverurl + '/500'
break
} }
})
socket.on('error', function (data) { setClientId (id) {
console.error(data) this.id = id
if (data.message && data.message.indexOf('AUTH failed') === 0) { location.href = serverurl + '/403' }
})
socket.on('delete', function () {
if (personalInfo.login) {
deleteServerHistory(noteid, function (err, data) {
if (!err) location.href = serverurl
})
} else {
getHistory(function (notehistory) {
var newnotehistory = removeHistory(noteid, notehistory)
saveHistory(newnotehistory)
location.href = serverurl
})
} }
})
socket.on('maintenance', function () { setConnectionStatus (status) {
cmClient.revision = -1 this.connectionStatus = status
}) // notify status changed
socket.on('disconnect', function (data) {
showStatus(statusType.offline) setTimeout(() => {
if (window.loaded) { notifyConnectionStatusChanged(this.connectionStatus)
saveInfo() }, 2000)
lastInfo.history = editor.getHistory()
} }
if (!editor.getOption('readOnly')) { editor.setOption('readOnly', true) } }
if (!retryTimer) {
retryTimer = setInterval(function () { function notifyConnectionStatusChanged (newStatus) {
if (!needRefresh) socket.connect() showStatus(newStatus)
}, 1000) }
}
}) function onSocketConnectHandler () {
socket.on('reconnect', function (data) { // used for reconnection socket io server if user disconnect or has network problem
// sync back any change in offline
emitUserStatus(true)
cursorActivity(editor)
socket.emit('online users')
})
socket.on('connect', function (data) {
clearInterval(retryTimer) clearInterval(retryTimer)
retryTimer = null retryTimer = null
console.log(`connect to server, client id is: ${socket.id}`)
personalInfo['id'] = socket.id personalInfo['id'] = socket.id
showStatus(statusType.connected) // set changed
getApplicationState().setConnectionStatus(statusType.connected)
// emit version to check this client is valid
socket.emit('version') socket.emit('version')
}) }
socket.on('version', function (data) {
if (version !== data.version) { function onDocHandler (event) {
if (version < data.minimumCompatibleVersion) { console.log(event)
setRefreshModal('incompatible-version') var noteContent = event.str
setNeedRefresh() var isNoteContentMismatch = editor.getValue() !== noteContent
} else { var setDoc = !cmClient || (cmClient && (cmClient.revision === -1 || (cmClient.revision !== event.revision && !havePendingOperation()))) || event.force
setRefreshModal('new-version')
}
}
})
socket.on('check', function (data) {
// console.log(data);
updateInfo(data)
})
socket.on('permission', function (data) {
updatePermission(data.permission)
})
socket.on('refresh', function (data) {
// console.log(data);
editorInstance.config.docmaxlength = data.docmaxlength
editor.setOption('maxLength', editorInstance.config.docmaxlength)
updateInfo(data)
updatePermission(data.permission)
if (!window.loaded) {
// auto change mode if no content detected
var nocontent = editor.getValue().length <= 0
if (nocontent) {
if (visibleXS) { appState.currentMode = modeType.edit } else { appState.currentMode = modeType.both }
}
// parse mode from url
if (window.location.search.length > 0) {
var urlMode = modeType[window.location.search.substr(1)]
if (urlMode) appState.currentMode = urlMode
}
changeMode(appState.currentMode)
if (nocontent && !visibleXS) {
editor.focus()
editor.refresh()
}
updateViewInner() // bring up view rendering earlier
updateHistory() // update history whether have content or not
window.loaded = true
emitUserStatus() // send first user status
updateOnlineStatus() // update first online status
setTimeout(function () {
// work around editor not refresh or doc not fully loaded
windowResizeInner()
// work around might not scroll to hash
scrollToHash()
}, 1)
}
if (editor.getOption('readOnly')) { editor.setOption('readOnly', false) }
})
socket.on('doc', function (obj) {
console.log(obj)
var body = obj.str
var bodyMismatch = editor.getValue() !== body
var setDoc = !cmClient || (cmClient && (cmClient.revision === -1 || (cmClient.revision !== obj.revision && !havePendingOperation()))) || obj.force
saveInfo() saveInfo()
if (setDoc && bodyMismatch) {
if (setDoc && isNoteContentMismatch) {
if (cmClient) cmClient.editorAdapter.ignoreNextChange = true if (cmClient) cmClient.editorAdapter.ignoreNextChange = true
if (body) editor.setValue(body) if (noteContent) editor.setValue(noteContent)
else editor.setValue('') else editor.setValue('')
} }
@ -581,113 +512,288 @@ function registerSocketIO () {
ui.content.fadeIn() ui.content.fadeIn()
} else { } else {
// if current doc is equal to the doc before disconnect // if current doc is equal to the doc before disconnect
if (setDoc && bodyMismatch) editor.clearHistory() if (setDoc && isNoteContentMismatch) editor.clearHistory()
else if (lastInfo.history) editor.setHistory(lastInfo.history) else if (lastInfo.history) editor.setHistory(lastInfo.history)
lastInfo.history = null lastInfo.history = null
} }
if (!cmClient) { if (!cmClient) {
cmClient = window.cmClient = new EditorClient( cmClient = window.cmClient = new EditorClient(
obj.revision, obj.clients, event.revision, event.clients,
new SocketIOAdapter(socket), new CodeMirrorAdapter(editor) new SocketIOAdapter(socket), new CodeMirrorAdapter(editor)
) )
synchronized_ = cmClient.state synchronized_ = cmClient.state
} else if (setDoc) { } else if (setDoc) {
if (bodyMismatch) { if (isNoteContentMismatch) {
cmClient.undoManager.undoStack.length = 0 cmClient.undoManager.undoStack.length = 0
cmClient.undoManager.redoStack.length = 0 cmClient.undoManager.redoStack.length = 0
} }
cmClient.revision = obj.revision cmClient.revision = event.revision
cmClient.setState(synchronized_) cmClient.setState(synchronized_)
cmClient.initializeClientList() cmClient.initializeClientList()
cmClient.initializeClients(obj.clients) cmClient.initializeClients(event.clients)
} else if (havePendingOperation()) { } else if (havePendingOperation()) {
cmClient.serverReconnect() cmClient.serverReconnect()
} }
if (setDoc && bodyMismatch) { if (setDoc && isNoteContentMismatch) {
isDirty = true isDirty = true
updateView() updateView()
} }
restoreInfo() restoreInfo()
}) }
socket.on('ack', function () {
isDirty = true function registerSocketIO () {
updateView() const realtimeClient = new RealtimeClient()
}) socket = realtimeClient.socket
socket.on('operation', function () { console.log(`before connect to server, client id is: ${socket.id}`)
isDirty = true
updateView() realtimeClient.on('connect', onSocketConnectHandler) // event 1
}) realtimeClient.on('doc', onDocHandler) // event 2
socket.on('online users', function (data) {
if (debug) { console.debug(data) } // socket.on('connect', function (data) {
onlineUsers = data.users // clearInterval(retryTimer)
updateOnlineStatus() // retryTimer = null
$('.CodeMirror-other-cursors').children().each(function (key, value) { // personalInfo['id'] = socket.id
var found = false // showStatus(statusType.connected)
for (var i = 0; i < data.users.length; i++) { // socket.emit('version')
var user = data.users[i] // })
if ($(this).attr('id') === user.id) { found = true }
} // socket.on('reconnect', function (data) {
if (!found) { // // sync back any change in offline
$(this).stop(true).fadeOut('normal', function () { // emitUserStatus(true)
$(this).remove() // cursorActivity(editor)
}) // socket.emit('online users')
} // })
})
for (var i = 0; i < data.users.length; i++) { // socket.on('info', function (data) {
var user = data.users[i] // console.error(data)
if (user.id !== socket.id) { buildCursor(user) } else { personalInfo = user } // switch (data.code) {
} // case 403:
}) // location.href = serverurl + '/403'
socket.on('user status', function (data) { // break
if (debug) { console.debug(data) } // case 404:
for (var i = 0; i < onlineUsers.length; i++) { // location.href = serverurl + '/404'
if (onlineUsers[i].id === data.id) { // break
onlineUsers[i] = data // case 500:
} // location.href = serverurl + '/500'
} // break
updateOnlineStatus() // }
if (data.id !== socket.id) { buildCursor(data) } // })
}) // socket.on('error', function (data) {
socket.on('cursor focus', function (data) { // console.error(data)
if (debug) { console.debug(data) } // if (data.message && data.message.indexOf('AUTH failed') === 0) { location.href = serverurl + '/403' }
for (var i = 0; i < onlineUsers.length; i++) { // })
if (onlineUsers[i].id === data.id) { // socket.on('delete', function () {
onlineUsers[i].cursor = data.cursor // if (personalInfo.login) {
} // deleteServerHistory(noteid, function (err, data) {
} // if (!err) location.href = serverurl
if (data.id !== socket.id) { buildCursor(data) } // })
// force show // } else {
var cursor = $('div[data-clientid="' + data.id + '"]') // getHistory(function (notehistory) {
if (cursor.length > 0) { // var newnotehistory = removeHistory(noteid, notehistory)
cursor.stop(true).fadeIn() // saveHistory(newnotehistory)
} // location.href = serverurl
}) // })
socket.on('cursor activity', function (data) { // }
if (debug) { console.debug(data) } // })
for (var i = 0; i < onlineUsers.length; i++) { // socket.on('maintenance', function () {
if (onlineUsers[i].id === data.id) { // cmClient.revision = -1
onlineUsers[i].cursor = data.cursor // })
} // socket.on('disconnect', function (data) {
} // showStatus(statusType.offline)
if (data.id !== socket.id) { buildCursor(data) } // if (window.loaded) {
}) // saveInfo()
socket.on('cursor blur', function (data) { // lastInfo.history = editor.getHistory()
if (debug) { console.debug(data) } // }
for (var i = 0; i < onlineUsers.length; i++) { // if (!editor.getOption('readOnly')) { editor.setOption('readOnly', true) }
if (onlineUsers[i].id === data.id) { // if (!retryTimer) {
onlineUsers[i].cursor = null // retryTimer = setInterval(function () {
} // if (!needRefresh) socket.connect()
} // }, 1000)
if (data.id !== socket.id) { buildCursor(data) } // }
// force hide // })
var cursor = $('div[data-clientid="' + data.id + '"]')
if (cursor.length > 0) { // socket.on('version', function (data) {
cursor.stop(true).fadeOut() // if (version !== data.version) {
} // if (version < data.minimumCompatibleVersion) {
}) // setRefreshModal('incompatible-version')
// setNeedRefresh()
// } else {
// setRefreshModal('new-version')
// }
// }
// })
// socket.on('check', function (data) {
// // console.log(data);
// updateInfo(data)
// })
// socket.on('permission', function (data) {
// updatePermission(data.permission)
// })
// socket.on('refresh', function (data) {
// // console.log(data);
// editorInstance.config.docmaxlength = data.docmaxlength
// editor.setOption('maxLength', editorInstance.config.docmaxlength)
// updateInfo(data)
// updatePermission(data.permission)
// if (!window.loaded) {
// // auto change mode if no content detected
// var nocontent = editor.getValue().length <= 0
// if (nocontent) {
// if (visibleXS) { appState.currentMode = modeType.edit } else { appState.currentMode = modeType.both }
// }
// // parse mode from url
// if (window.location.search.length > 0) {
// var urlMode = modeType[window.location.search.substr(1)]
// if (urlMode) appState.currentMode = urlMode
// }
// changeMode(appState.currentMode)
// if (nocontent && !visibleXS) {
// editor.focus()
// editor.refresh()
// }
// updateViewInner() // bring up view rendering earlier
// updateHistory() // update history whether have content or not
// window.loaded = true
// emitUserStatus() // send first user status
// updateOnlineStatus() // update first online status
// setTimeout(function () {
// // work around editor not refresh or doc not fully loaded
// windowResizeInner()
// // work around might not scroll to hash
// scrollToHash()
// }, 1)
// }
// if (editor.getOption('readOnly')) { editor.setOption('readOnly', false) }
// })
// socket.on('doc', function (obj) {
// console.log(obj)
// var body = obj.str
// var bodyMismatch = editor.getValue() !== body
// var setDoc = !cmClient || (cmClient && (cmClient.revision === -1 || (cmClient.revision !== obj.revision && !havePendingOperation()))) || obj.force
//
// saveInfo()
// if (setDoc && bodyMismatch) {
// if (cmClient) cmClient.editorAdapter.ignoreNextChange = true
// if (body) editor.setValue(body)
// else editor.setValue('')
// }
//
// if (!window.loaded) {
// editor.clearHistory()
// ui.spinner.hide()
// ui.content.fadeIn()
// } else {
// // if current doc is equal to the doc before disconnect
// if (setDoc && bodyMismatch) editor.clearHistory()
// else if (lastInfo.history) editor.setHistory(lastInfo.history)
// lastInfo.history = null
// }
//
// if (!cmClient) {
// cmClient = window.cmClient = new EditorClient(
// obj.revision, obj.clients,
// new SocketIOAdapter(socket), new CodeMirrorAdapter(editor)
// )
// synchronized_ = cmClient.state
// } else if (setDoc) {
// if (bodyMismatch) {
// cmClient.undoManager.undoStack.length = 0
// cmClient.undoManager.redoStack.length = 0
// }
// cmClient.revision = obj.revision
// cmClient.setState(synchronized_)
// cmClient.initializeClientList()
// cmClient.initializeClients(obj.clients)
// } else if (havePendingOperation()) {
// cmClient.serverReconnect()
// }
//
// if (setDoc && bodyMismatch) {
// isDirty = true
// updateView()
// }
//
// restoreInfo()
// })
// socket.on('ack', function () {
// isDirty = true
// updateView()
// })
// socket.on('operation', function () {
// isDirty = true
// updateView()
// })
// socket.on('online users', function (data) {
// if (debug) { console.debug(data) }
// onlineUsers = data.users
// updateOnlineStatus()
// $('.CodeMirror-other-cursors').children().each(function (key, value) {
// var found = false
// for (var i = 0; i < data.users.length; i++) {
// var user = data.users[i]
// if ($(this).attr('id') === user.id) { found = true }
// }
// if (!found) {
// $(this).stop(true).fadeOut('normal', function () {
// $(this).remove()
// })
// }
// })
// for (var i = 0; i < data.users.length; i++) {
// var user = data.users[i]
// if (user.id !== socket.id) { buildCursor(user) } else { personalInfo = user }
// }
// })
// socket.on('user status', function (data) {
// if (debug) { console.debug(data) }
// for (var i = 0; i < onlineUsers.length; i++) {
// if (onlineUsers[i].id === data.id) {
// onlineUsers[i] = data
// }
// }
// updateOnlineStatus()
// if (data.id !== socket.id) { buildCursor(data) }
// })
// socket.on('cursor focus', function (data) {
// if (debug) { console.debug(data) }
// for (var i = 0; i < onlineUsers.length; i++) {
// if (onlineUsers[i].id === data.id) {
// onlineUsers[i].cursor = data.cursor
// }
// }
// if (data.id !== socket.id) { buildCursor(data) }
// // force show
// var cursor = $('div[data-clientid="' + data.id + '"]')
// if (cursor.length > 0) {
// cursor.stop(true).fadeIn()
// }
// })
// socket.on('cursor activity', function (data) {
// if (debug) { console.debug(data) }
// for (var i = 0; i < onlineUsers.length; i++) {
// if (onlineUsers[i].id === data.id) {
// onlineUsers[i].cursor = data.cursor
// }
// }
// if (data.id !== socket.id) { buildCursor(data) }
// })
// socket.on('cursor blur', function (data) {
// if (debug) { console.debug(data) }
// for (var i = 0; i < onlineUsers.length; i++) {
// if (onlineUsers[i].id === data.id) {
// onlineUsers[i].cursor = null
// }
// }
// if (data.id !== socket.id) { buildCursor(data) }
// // force hide
// var cursor = $('div[data-clientid="' + data.id + '"]')
// if (cursor.length > 0) {
// cursor.stop(true).fadeOut()
// }
// })
} }
// --------------------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------------------
@ -807,7 +913,7 @@ var wasFocus = false
// updateHistoryInner(); // updateHistoryInner();
// }) // })
// $(window).on('error', function () { // $(window).on('error', function () {
// setNeedRefresh(); // setNeedRefresh();
// }) // })
// setupSyncAreas(ui.area.codemirrorScroll, ui.area.view, ui.area.markdown, editor) // setupSyncAreas(ui.area.codemirrorScroll, ui.area.view, ui.area.markdown, editor)
@ -2034,13 +2140,28 @@ class RealtimeClient {
* overwrite the original event for checking login state * overwrite the original event for checking login state
*/ */
patchSocket () { patchSocket () {
const onevent = this.socket.onevent
this.socket.onevent = function (packet) {
console.log(`received ${packet.data[0]} event -------- on event`)
var args = packet.data || []
onevent.call(this, packet) // original call
packet.data = ['*'].concat(args)
onevent.call(this, packet) // additional call to catch-all
}
// patch on / emit event
const on = this.socket.on const on = this.socket.on
const emit = this.socket.emit const emit = this.socket.emit
const skipEvent = () => needRefresh || checkLoginStateChanged() const skipEvent = () => needRefresh || checkLoginStateChanged()
const socket = this.socket
this.socket.on = function () { this.socket.on = function () {
if (skipEvent()) return if (skipEvent()) return
on.apply(socket, arguments) const eventName = arguments[0]
const func = arguments[1]
on.call(socket, eventName, function () {
console.log(`received ${eventName} event.`)
func.apply(this, arguments)
})
} }
this.socket.emit = function () { this.socket.emit = function () {
@ -2049,6 +2170,10 @@ class RealtimeClient {
} }
} }
on (eventName, handler) {
this.socket.on(eventName, handler)
}
registerEvent () { registerEvent () {
// socket.io connection event // socket.io connection event
this.socket.on('connect') this.socket.on('connect')