RED.multiplayer = (function () {
// activeSessionId - used to identify sessions across websocket reconnects
let activeSessionId
let headerWidget
// Map of session id to { session:'', user:{}, location:{}}
let sessions = {}
// Map of username to { user:{}, sessions:[] }
let users = {}
function addUserSession (session) {
if (sessions[session.session]) {
// This is an existing connection that has been authenticated
const existingSession = sessions[session.session]
if (existingSession.user.username !== session.user.username) {
removeUserHeaderButton(users[existingSession.user.username])
}
}
sessions[session.session] = session
const user = users[session.user.username] = users[session.user.username] || {
user: session.user,
sessions: []
}
if (session.user.profileColor === undefined) {
session.user.profileColor = (1 + Math.floor(Math.random() * 5))
}
session.location = session.location || {}
user.sessions.push(session)
if (session.session === activeSessionId) {
// This is the current user session - do not add a extra button for them
} else {
if (user.sessions.length === 1) {
if (user.button) {
clearTimeout(user.inactiveTimeout)
clearTimeout(user.removeTimeout)
user.button.removeClass('inactive')
} else {
addUserHeaderButton(user)
}
}
sessions[session.session].location = session.location
updateUserLocation(session.session)
}
}
function removeUserSession (sessionId, isDisconnected) {
removeUserLocation(sessionId)
const session = sessions[sessionId]
delete sessions[sessionId]
const user = users[session.user.username]
const i = user.sessions.indexOf(session)
user.sessions.splice(i, 1)
if (isDisconnected) {
removeUserHeaderButton(user)
} else {
if (user.sessions.length === 0) {
// Give the user 5s to reconnect before marking inactive
user.inactiveTimeout = setTimeout(() => {
user.button.addClass('inactive')
// Give the user further 20 seconds to reconnect before removing them
// from the user toolbar entirely
user.removeTimeout = setTimeout(() => {
removeUserHeaderButton(user)
}, 20000)
}, 5000)
}
}
}
function addUserHeaderButton (user) {
user.button = $('
')
.attr('data-username', user.user.username)
.prependTo("#red-ui-multiplayer-user-list");
var button = user.button.find("button")
RED.popover.tooltip(button, user.user.username)
button.on('click', function () {
const location = user.sessions[0].location
revealUser(location)
})
const userProfile = RED.user.generateUserIcon(user.user)
userProfile.appendTo(button)
}
function removeUserHeaderButton (user) {
user.button.remove()
delete user.button
}
function getLocation () {
const location = {
workspace: RED.workspaces.active()
}
const editStack = RED.editor.getEditStack()
for (let i = editStack.length - 1; i >= 0; i--) {
if (editStack[i].id) {
location.node = editStack[i].id
break
}
}
return location
}
function publishLocation () {
const location = getLocation()
if (location.workspace !== 0) {
log('send', 'multiplayer/location', location)
RED.comms.send('multiplayer/location', location)
}
}
function revealUser(location, skipWorkspace) {
if (location.node) {
// Need to check if this is a known node, so we can fall back to revealing
// the workspace instead
const node = RED.nodes.node(location.node)
if (node) {
RED.view.reveal(location.node)
} else if (!skipWorkspace && location.workspace) {
RED.view.reveal(location.workspace)
}
} else if (!skipWorkspace && location.workspace) {
RED.view.reveal(location.workspace)
}
}
const workspaceTrays = {}
function getWorkspaceTray(workspaceId) {
// console.log('get tray for',workspaceId)
if (!workspaceTrays[workspaceId]) {
const tray = $('')
const users = []
const userIcons = {}
const userCountIcon = $(`