348 lines
No EOL
11 KiB
JavaScript
348 lines
No EOL
11 KiB
JavaScript
//
|
|
// statusIndicatorClient.js
|
|
//
|
|
// Created by Robin Wilson on 2019-04-02
|
|
// Copyright 2019 High Fidelity, Inc.
|
|
//
|
|
// Distributed under the Apache License, Version 2.0.
|
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
|
|
(function () {
|
|
var DEBUG = false;
|
|
|
|
// #region STATUS OVERLAY
|
|
|
|
// Create clickable status overlay on desktop
|
|
// In upper right hand corner of screen
|
|
// Displays Available or Busy and toggles after click
|
|
var desktopOverlay,
|
|
rectangleOverlay,
|
|
// desktop overlayOptions
|
|
OVERLAY_WINDOW_RIGHT_PADDING = 10,
|
|
OVERLAY_WIDTH = 10,
|
|
OVERLAY_FONT_SIZE = 20.0,
|
|
OVERLAY_WIDTH = 200,
|
|
// rectangleOverlay options
|
|
OVERLAY_RECTANGLE_LEFT_PADDING = 10,
|
|
OVERLAY_RECTANGLE_TOP_PADDING = 12,
|
|
OVERLAY_RECTANGLE_WIDTH_HEIGHT = 20,
|
|
AVAILABLE_COLOR = { red: 0, green: 144, blue: 54 },
|
|
NOT_AVAILABLE_COLOR = { red: 255, green: 0, blue: 26 },
|
|
OTHER_COLOR = { red: 255, green: 237, blue: 0 },
|
|
TEXT_OVERLAY_PROPS = {
|
|
width: OVERLAY_RECTANGLE_WIDTH_HEIGHT,
|
|
height: OVERLAY_RECTANGLE_WIDTH_HEIGHT,
|
|
y: OVERLAY_FONT_SIZE + OVERLAY_RECTANGLE_TOP_PADDING,
|
|
color: { red: 255, green: 255, blue: 255 },
|
|
alpha: 1.0,
|
|
visible: true
|
|
},
|
|
RECTANGLE_OVERLAY_PROPS = {
|
|
width: OVERLAY_WIDTH,
|
|
height: OVERLAY_FONT_SIZE * 2.2,
|
|
y: OVERLAY_FONT_SIZE,
|
|
lineHeight: OVERLAY_FONT_SIZE,
|
|
margin: OVERLAY_FONT_SIZE / 2,
|
|
font: { size: OVERLAY_FONT_SIZE },
|
|
backgroundColor: { red: 17, green: 17, blue: 17 },
|
|
alpha: 1.0,
|
|
backgroundAlpha: 0.7,
|
|
visible: true
|
|
};
|
|
|
|
function drawStatusOverlays() {
|
|
var windowWidth = Window.innerWidth;
|
|
|
|
var desktopOverlayProps = RECTANGLE_OVERLAY_PROPS;
|
|
desktopOverlayProps.x = windowWidth - OVERLAY_WIDTH - OVERLAY_WINDOW_RIGHT_PADDING;
|
|
desktopOverlay = Overlays.addOverlay("text", desktopOverlayProps);
|
|
|
|
var rectangleOverlayProps = TEXT_OVERLAY_PROPS;
|
|
rectangleOverlayProps.x = windowWidth - OVERLAY_WIDTH + OVERLAY_RECTANGLE_LEFT_PADDING - OVERLAY_WINDOW_RIGHT_PADDING;
|
|
if (currentStatus === "available") {
|
|
rectangleOverlayProps.color = AVAILABLE_COLOR;
|
|
} else if (currentStatus === "busy") {
|
|
rectangleOverlayProps.color = NOT_AVAILABLE_COLOR;
|
|
} else {
|
|
rectangleOverlayProps.color = OTHER_COLOR;
|
|
}
|
|
rectangleOverlay = Overlays.addOverlay("rectangle", rectangleOverlayProps);
|
|
}
|
|
|
|
|
|
// Delete clickable status overlay on desktop
|
|
function deleteStatusOverlays() {
|
|
if (rectangleOverlay) {
|
|
Overlays.deleteOverlay(rectangleOverlay);
|
|
rectangleOverlay = false;
|
|
}
|
|
if (desktopOverlay) {
|
|
Overlays.deleteOverlay(desktopOverlay);
|
|
desktopOverlay = false;
|
|
}
|
|
}
|
|
|
|
// #endregion STATUS OVERLAY
|
|
|
|
|
|
// #region HEARTBEAT
|
|
|
|
// Send heartbeat with updates to database
|
|
// When this stops, the database will set status to Offline
|
|
var HEARTBEAT_TIMEOUT_MS = 5000,
|
|
heartbeat;
|
|
function startHeartbeatTimer() {
|
|
console.log("Current status: " + currentStatus);
|
|
|
|
if (heartbeat) {
|
|
Script.clearTimeout(heartbeat);
|
|
heartbeat = false;
|
|
}
|
|
|
|
heartbeat = Script.setTimeout(function() {
|
|
heartbeat = false;
|
|
updateStatus();
|
|
}, HEARTBEAT_TIMEOUT_MS);
|
|
}
|
|
|
|
// #endregion HEARTBEAT
|
|
|
|
|
|
// #region SEND/GET STATUS REQUEST
|
|
|
|
function sendStatusUpdate() {
|
|
var queryParamString = "type=heartbeat";
|
|
queryParamString += "&username=" + AccountServices.username;
|
|
queryParamString += "&displayName=" + MyAvatar.displayName;
|
|
queryParamString += "&status=";
|
|
queryParamString += currentStatus;
|
|
|
|
var uri = REQUEST_URL + "?" + queryParamString;
|
|
|
|
if (DEBUG) {
|
|
console.log("sendStatusUpdate: " + uri);
|
|
}
|
|
|
|
request({
|
|
uri: uri
|
|
}, function (error, response) {
|
|
startHeartbeatTimer();
|
|
|
|
if (error || !response || response.status !== "success") {
|
|
console.error("Error with updateStatus: " + JSON.stringify(response));
|
|
return;
|
|
}
|
|
});
|
|
}
|
|
|
|
// Get status from database
|
|
function getStatusUpdate(callback) {
|
|
var queryParamString = "type=getStatus";
|
|
queryParamString += "&username=" + AccountServices.username;
|
|
|
|
var uri = REQUEST_URL + "?" + queryParamString;
|
|
|
|
if (DEBUG) {
|
|
console.log("getStatusUpdate: " + uri);
|
|
}
|
|
|
|
request({
|
|
uri: uri
|
|
}, function (error, response) {
|
|
if (error || !response || response.status !== "success") {
|
|
console.error("Error with getStatus: " + JSON.stringify(response));
|
|
} else if (response.data.userStatus.toLowerCase() !== "offline") {
|
|
currentStatus = response.data.userStatus;
|
|
editStatusOverlays();
|
|
}
|
|
|
|
callback();
|
|
});
|
|
}
|
|
|
|
|
|
// Sends status to database
|
|
var request = Script.require('request').request,
|
|
REQUEST_URL = Script.require(Script.resolvePath('./secrets.json')).REQUEST_URL;
|
|
function updateStatus(forceUpdateOnly) {
|
|
if (heartbeat) {
|
|
Script.clearTimeout(heartbeat);
|
|
heartbeat = false;
|
|
}
|
|
|
|
if (forceUpdateOnly) {
|
|
sendStatusUpdate();
|
|
} else {
|
|
getStatusUpdate(sendStatusUpdate);
|
|
}
|
|
}
|
|
|
|
|
|
// #endregion SEND/GET STATUS REQUEST
|
|
|
|
|
|
// #region SIGNALS
|
|
|
|
// On mouse press, check if the status overlay on desktop was clicked
|
|
// If yes, change status to the opposite and send status update
|
|
var currentStatus = "available"; // Default is available
|
|
function onMousePressEvent(event) {
|
|
if (DEBUG) {
|
|
console.log("onMousePressEvent isLeftButton: " + event.isLeftButton);
|
|
}
|
|
// is primary button
|
|
var overlayID = Overlays.getOverlayAtPoint({ x: event.x, y: event.y });
|
|
if (event.isLeftButton && overlayID && (overlayID === rectangleOverlay || overlayID === desktopOverlay)) {
|
|
if (DEBUG) {
|
|
console.log("onMousePressEvent status overlay has been clicked");
|
|
}
|
|
if (currentStatus === "available") {
|
|
currentStatus = "busy";
|
|
} else if (currentStatus === "busy") {
|
|
currentStatus = "available";
|
|
} else {
|
|
currentStatus = "busy";
|
|
}
|
|
editStatusOverlaysAndSendUpdate();
|
|
}
|
|
}
|
|
|
|
|
|
var MAX_STATUS_LENGTH_CHARS = 9;
|
|
function editStatusOverlays() {
|
|
var edits = {};
|
|
|
|
var rectangleColor;
|
|
if (currentStatus === "available") {
|
|
rectangleColor = AVAILABLE_COLOR;
|
|
} else if (currentStatus === "busy") {
|
|
rectangleColor = NOT_AVAILABLE_COLOR;
|
|
} else {
|
|
rectangleColor = OTHER_COLOR;
|
|
}
|
|
edits[rectangleOverlay] = { "color": rectangleColor }
|
|
|
|
// For long statuses cut and append "..."
|
|
var statusText = currentStatus;
|
|
if (statusText.length > MAX_STATUS_LENGTH_CHARS) {
|
|
statusText = currentStatus.substring(0, 9) + "...";
|
|
}
|
|
|
|
// Allow space for the rectangle color
|
|
edits[desktopOverlay] = { "text": (" " + statusText) }
|
|
|
|
Overlays.editOverlays(edits);
|
|
}
|
|
|
|
|
|
function editStatusOverlaysAndSendUpdate() {
|
|
editStatusOverlays();
|
|
updateStatus(true);
|
|
}
|
|
|
|
|
|
// When window resizes, redraw overlays
|
|
function onWindowResize() {
|
|
deleteStatusOverlays();
|
|
drawStatusOverlays();
|
|
}
|
|
|
|
|
|
// When avatar becomes active from being away
|
|
// Set status back to previousStatus
|
|
function onWentActive() {
|
|
currentStatus = previousStatus;
|
|
editStatusOverlaysAndSendUpdate();
|
|
}
|
|
|
|
|
|
// When avatar goes away, set status to busy
|
|
var previousStatus;
|
|
function onWentAway() {
|
|
previousStatus = currentStatus;
|
|
currentStatus = "busy";
|
|
editStatusOverlaysAndSendUpdate();
|
|
}
|
|
|
|
|
|
// Delete overlays when display mode changes to HMD mode
|
|
// Draw overlays when mode is in desktop
|
|
function onDisplayModeChanged(isHMDMode) {
|
|
if (isHMDMode) {
|
|
deleteStatusOverlays();
|
|
} else {
|
|
drawStatusOverlays();
|
|
}
|
|
}
|
|
|
|
|
|
// Domain changed update avatar location
|
|
function onDomainChanged(domainName) {
|
|
var queryParamString = "type=setUserLocation";
|
|
queryParamString += "&username=" + AccountServices.username;
|
|
queryParamString += "&location=" + domainName;
|
|
|
|
var uri = REQUEST_URL + "?" + queryParamString;
|
|
|
|
if (DEBUG) {
|
|
console.log("statusIndicator onDomainChanged: " + uri);
|
|
}
|
|
|
|
request({
|
|
uri: uri
|
|
}, function (error, response) {
|
|
if (error || !response || response.status !== "success") {
|
|
console.error("Error with onDomainChanged: " + JSON.stringify(response));
|
|
} else {
|
|
// successfully sent updateLocation
|
|
if (DEBUG) {
|
|
console.log("Entered onDomainChanged called: " + zoneName);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// #endregion SIGNALS
|
|
|
|
|
|
// #region APP LIFETIME
|
|
|
|
// Creates the app button and sets up signals and hearbeat
|
|
function startup() {
|
|
if (!HMD.active) {
|
|
drawStatusOverlays();
|
|
}
|
|
|
|
Script.scriptEnding.connect(unload);
|
|
Controller.mousePressEvent.connect(onMousePressEvent);
|
|
Window.geometryChanged.connect(onWindowResize);
|
|
MyAvatar.wentAway.connect(onWentAway);
|
|
MyAvatar.wentActive.connect(onWentActive);
|
|
MyAvatar.displayNameChanged.connect(updateStatus);
|
|
HMD.displayModeChanged.connect(onDisplayModeChanged);
|
|
Window.domainChanged.connect(onDomainChanged);
|
|
|
|
updateStatus();
|
|
}
|
|
|
|
|
|
// Cleans up timeouts, signals, and overlays
|
|
function unload() {
|
|
deleteStatusOverlays();
|
|
Controller.mousePressEvent.disconnect(onMousePressEvent);
|
|
Window.geometryChanged.disconnect(onWindowResize);
|
|
MyAvatar.wentAway.disconnect(onWentAway);
|
|
MyAvatar.wentActive.disconnect(onWentActive);
|
|
MyAvatar.displayNameChanged.disconnect(updateStatus);
|
|
HMD.displayModeChanged.disconnect(onDisplayModeChanged);
|
|
Window.domainChanged.disconnect(onDomainChanged);
|
|
if (heartbeat) {
|
|
Script.clearTimeout(heartbeat);
|
|
heartbeat = false;
|
|
}
|
|
}
|
|
|
|
// #endregion APP LIFETIME
|
|
|
|
startup();
|
|
})(); |