content/hifi-content/robin/dev/usefulUtils/statusIndicator/statusIndicator.js
2022-02-14 02:04:11 +01:00

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();
})();