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 editor = null
function getEditorInstance () { return editorInstance}
function getEditorInstance () { return editorInstance }
function setEditorInstance (e) {
editorInstance = e
editor = editorInstance.editor
@ -224,6 +226,13 @@ function getEditor () {
return null
}
/**
* @returns ApplicationState
*/
function getApplicationState () {
return applicationState
}
// ---------------------------------------------------------------------------------------------------
/**
@ -233,9 +242,9 @@ function getEditor () {
$(document).ready(function () {
console.log('-- first phase --')
applicationState = new ApplicationState()
initCodeMirrorEditor()
registerCodeMirrorEvent()
// registerCodeMirrorEvent()
registerSocketIO()
// var inlineAttach = inlineAttachment.editors.codemirror4.attach(editor)
defaultTextHeight = parseInt($('.CodeMirror').css('line-height'))
@ -308,7 +317,7 @@ $(document).ready(function () {
})
})
function initCodeMirrorEditor() {
function initCodeMirrorEditor () {
const textit = document.getElementById('textit')
if (!textit) {
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 () {
const realtimeClient = new RealtimeClient()
socket = realtimeClient.socket
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) {
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
console.log(`before connect to server, client id is: ${socket.id}`)
saveInfo()
if (setDoc && bodyMismatch) {
if (cmClient) cmClient.editorAdapter.ignoreNextChange = true
if (body) editor.setValue(body)
else editor.setValue('')
}
realtimeClient.on('connect', onSocketConnectHandler) // event 1
realtimeClient.on('doc', onDocHandler) // event 2
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
}
// socket.on('connect', function (data) {
// clearInterval(retryTimer)
// retryTimer = null
// personalInfo['id'] = socket.id
// showStatus(statusType.connected)
// socket.emit('version')
// })
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()
}
// socket.on('reconnect', function (data) {
// // sync back any change in offline
// emitUserStatus(true)
// cursorActivity(editor)
// socket.emit('online users')
// })
if (setDoc && bodyMismatch) {
isDirty = true
updateView()
}
// 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) {
// 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('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()
}
})
// 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()
// 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();
// })
// $(window).on('error', function () {
// setNeedRefresh();
// setNeedRefresh();
// })
// 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
*/
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 emit = this.socket.emit
const skipEvent = () => needRefresh || checkLoginStateChanged()
const socket = this.socket
this.socket.on = function () {
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 () {
@ -2049,6 +2170,10 @@ class RealtimeClient {
}
}
on (eventName, handler) {
this.socket.on(eventName, handler)
}
registerEvent () {
// socket.io connection event
this.socket.on('connect')