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,248 +455,345 @@ function registerCodeMirrorEvent () {
}) })
} }
class ApplicationState {
constructor () {
this.connectionStatus = statusType.connected
}
setClientId (id) {
this.id = id
}
setConnectionStatus (status) {
this.connectionStatus = status
// notify status changed
setTimeout(() => {
notifyConnectionStatusChanged(this.connectionStatus)
}, 2000)
}
}
function notifyConnectionStatusChanged (newStatus) {
showStatus(newStatus)
}
function onSocketConnectHandler () {
// used for reconnection socket io server if user disconnect or has network problem
clearInterval(retryTimer)
retryTimer = null
console.log(`connect to server, client id is: ${socket.id}`)
personalInfo['id'] = socket.id
// set changed
getApplicationState().setConnectionStatus(statusType.connected)
// emit version to check this client is valid
socket.emit('version')
}
function onDocHandler (event) {
console.log(event)
var noteContent = event.str
var isNoteContentMismatch = editor.getValue() !== noteContent
var setDoc = !cmClient || (cmClient && (cmClient.revision === -1 || (cmClient.revision !== event.revision && !havePendingOperation()))) || event.force
saveInfo()
if (setDoc && isNoteContentMismatch) {
if (cmClient) cmClient.editorAdapter.ignoreNextChange = true
if (noteContent) editor.setValue(noteContent)
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 && isNoteContentMismatch) editor.clearHistory()
else if (lastInfo.history) editor.setHistory(lastInfo.history)
lastInfo.history = null
}
if (!cmClient) {
cmClient = window.cmClient = new EditorClient(
event.revision, event.clients,
new SocketIOAdapter(socket), new CodeMirrorAdapter(editor)
)
synchronized_ = cmClient.state
} else if (setDoc) {
if (isNoteContentMismatch) {
cmClient.undoManager.undoStack.length = 0
cmClient.undoManager.redoStack.length = 0
}
cmClient.revision = event.revision
cmClient.setState(synchronized_)
cmClient.initializeClientList()
cmClient.initializeClients(event.clients)
} else if (havePendingOperation()) {
cmClient.serverReconnect()
}
if (setDoc && isNoteContentMismatch) {
isDirty = true
updateView()
}
restoreInfo()
}
function registerSocketIO () { function registerSocketIO () {
const realtimeClient = new RealtimeClient() const realtimeClient = new RealtimeClient()
socket = realtimeClient.socket socket = realtimeClient.socket
socket.on('info', function (data) { console.log(`before connect to server, client id is: ${socket.id}`)
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) {
console.error(data)
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 () {
cmClient.revision = -1
})
socket.on('disconnect', function (data) {
showStatus(statusType.offline)
if (window.loaded) {
saveInfo()
lastInfo.history = editor.getHistory()
}
if (!editor.getOption('readOnly')) { editor.setOption('readOnly', true) }
if (!retryTimer) {
retryTimer = setInterval(function () {
if (!needRefresh) socket.connect()
}, 1000)
}
})
socket.on('reconnect', function (data) {
// sync back any change in offline
emitUserStatus(true)
cursorActivity(editor)
socket.emit('online users')
})
socket.on('connect', function (data) {
clearInterval(retryTimer)
retryTimer = null
personalInfo['id'] = socket.id
showStatus(statusType.connected)
socket.emit('version')
})
socket.on('version', function (data) {
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() realtimeClient.on('connect', onSocketConnectHandler) // event 1
if (setDoc && bodyMismatch) { realtimeClient.on('doc', onDocHandler) // event 2
if (cmClient) cmClient.editorAdapter.ignoreNextChange = true
if (body) editor.setValue(body)
else editor.setValue('')
}
if (!window.loaded) { // socket.on('connect', function (data) {
editor.clearHistory() // clearInterval(retryTimer)
ui.spinner.hide() // retryTimer = null
ui.content.fadeIn() // personalInfo['id'] = socket.id
} else { // showStatus(statusType.connected)
// if current doc is equal to the doc before disconnect // socket.emit('version')
if (setDoc && bodyMismatch) editor.clearHistory() // })
else if (lastInfo.history) editor.setHistory(lastInfo.history)
lastInfo.history = null
}
if (!cmClient) { // socket.on('reconnect', function (data) {
cmClient = window.cmClient = new EditorClient( // // sync back any change in offline
obj.revision, obj.clients, // emitUserStatus(true)
new SocketIOAdapter(socket), new CodeMirrorAdapter(editor) // cursorActivity(editor)
) // socket.emit('online users')
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) { // socket.on('info', function (data) {
isDirty = true // console.error(data)
updateView() // 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) {
// console.error(data)
// 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 () {
// cmClient.revision = -1
// })
// socket.on('disconnect', function (data) {
// showStatus(statusType.offline)
// if (window.loaded) {
// saveInfo()
// lastInfo.history = editor.getHistory()
// }
// if (!editor.getOption('readOnly')) { editor.setOption('readOnly', true) }
// if (!retryTimer) {
// retryTimer = setInterval(function () {
// if (!needRefresh) socket.connect()
// }, 1000)
// }
// })
restoreInfo() // socket.on('version', function (data) {
}) // if (version !== data.version) {
socket.on('ack', function () { // if (version < data.minimumCompatibleVersion) {
isDirty = true // setRefreshModal('incompatible-version')
updateView() // setNeedRefresh()
}) // } else {
socket.on('operation', function () { // setRefreshModal('new-version')
isDirty = true // }
updateView() // }
}) // })
socket.on('online users', function (data) { // socket.on('check', function (data) {
if (debug) { console.debug(data) } // // console.log(data);
onlineUsers = data.users // updateInfo(data)
updateOnlineStatus() // })
$('.CodeMirror-other-cursors').children().each(function (key, value) { // socket.on('permission', function (data) {
var found = false // updatePermission(data.permission)
for (var i = 0; i < data.users.length; i++) { // })
var user = data.users[i] // socket.on('refresh', function (data) {
if ($(this).attr('id') === user.id) { found = true } // // console.log(data);
} // editorInstance.config.docmaxlength = data.docmaxlength
if (!found) { // editor.setOption('maxLength', editorInstance.config.docmaxlength)
$(this).stop(true).fadeOut('normal', function () { // updateInfo(data)
$(this).remove() // updatePermission(data.permission)
}) // if (!window.loaded) {
} // // auto change mode if no content detected
}) // var nocontent = editor.getValue().length <= 0
for (var i = 0; i < data.users.length; i++) { // if (nocontent) {
var user = data.users[i] // if (visibleXS) { appState.currentMode = modeType.edit } else { appState.currentMode = modeType.both }
if (user.id !== socket.id) { buildCursor(user) } else { personalInfo = user } // }
} // // parse mode from url
}) // if (window.location.search.length > 0) {
socket.on('user status', function (data) { // var urlMode = modeType[window.location.search.substr(1)]
if (debug) { console.debug(data) } // if (urlMode) appState.currentMode = urlMode
for (var i = 0; i < onlineUsers.length; i++) { // }
if (onlineUsers[i].id === data.id) { // changeMode(appState.currentMode)
onlineUsers[i] = data // if (nocontent && !visibleXS) {
} // editor.focus()
} // editor.refresh()
updateOnlineStatus() // }
if (data.id !== socket.id) { buildCursor(data) } // updateViewInner() // bring up view rendering earlier
}) // updateHistory() // update history whether have content or not
socket.on('cursor focus', function (data) { // window.loaded = true
if (debug) { console.debug(data) } // emitUserStatus() // send first user status
for (var i = 0; i < onlineUsers.length; i++) { // updateOnlineStatus() // update first online status
if (onlineUsers[i].id === data.id) { // setTimeout(function () {
onlineUsers[i].cursor = data.cursor // // work around editor not refresh or doc not fully loaded
} // windowResizeInner()
} // // work around might not scroll to hash
if (data.id !== socket.id) { buildCursor(data) } // scrollToHash()
// force show // }, 1)
var cursor = $('div[data-clientid="' + data.id + '"]') // }
if (cursor.length > 0) { // if (editor.getOption('readOnly')) { editor.setOption('readOnly', false) }
cursor.stop(true).fadeIn() // })
} // socket.on('doc', function (obj) {
}) // console.log(obj)
socket.on('cursor activity', function (data) { // var body = obj.str
if (debug) { console.debug(data) } // var bodyMismatch = editor.getValue() !== body
for (var i = 0; i < onlineUsers.length; i++) { // var setDoc = !cmClient || (cmClient && (cmClient.revision === -1 || (cmClient.revision !== obj.revision && !havePendingOperation()))) || obj.force
if (onlineUsers[i].id === data.id) { //
onlineUsers[i].cursor = data.cursor // saveInfo()
} // if (setDoc && bodyMismatch) {
} // if (cmClient) cmClient.editorAdapter.ignoreNextChange = true
if (data.id !== socket.id) { buildCursor(data) } // if (body) editor.setValue(body)
}) // else editor.setValue('')
socket.on('cursor blur', function (data) { // }
if (debug) { console.debug(data) } //
for (var i = 0; i < onlineUsers.length; i++) { // if (!window.loaded) {
if (onlineUsers[i].id === data.id) { // editor.clearHistory()
onlineUsers[i].cursor = null // ui.spinner.hide()
} // ui.content.fadeIn()
} // } else {
if (data.id !== socket.id) { buildCursor(data) } // // if current doc is equal to the doc before disconnect
// force hide // if (setDoc && bodyMismatch) editor.clearHistory()
var cursor = $('div[data-clientid="' + data.id + '"]') // else if (lastInfo.history) editor.setHistory(lastInfo.history)
if (cursor.length > 0) { // lastInfo.history = null
cursor.stop(true).fadeOut() // }
} //
}) // 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')