Merge pull request #10258 from howard-stearns/watch-for-announcements

watch for announcements
This commit is contained in:
Howard Stearns 2017-04-27 14:00:39 -07:00 committed by GitHub
commit 88c3b4d9d3
5 changed files with 683 additions and 551 deletions

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
.st1{fill:#EF3B4E;}
</style>
<g id="Layer_2">
</g>
<g id="Layer_1_1_">
<path class="st0" d="M47.2,41.3l-9.1-9.1c-0.8-0.8-1.9-1.1-3-1l-2.4-2.4c1.8-2.6,2.8-5.7,2.8-9c0-8.9-7.2-16.1-16.1-16.1
S3.3,11,3.3,19.8c0,8.9,7.2,16.1,16.1,16.1c4.1,0,7.8-1.5,10.6-4l2.2,2.2c-0.2,1.1,0.1,2.2,1,3l9.1,9.1c1.4,1.4,3.6,1.4,4.9,0
C48.5,44.9,48.5,42.7,47.2,41.3z M19.4,32.2c-6.8,0-12.3-5.5-12.3-12.3S12.6,7.6,19.4,7.6s12.3,5.5,12.3,12.3
C31.8,26.6,26.2,32.2,19.4,32.2z"/>
</g>
<circle class="st1" cx="43.5" cy="6.5" r="5.9"/>
</svg>

After

Width:  |  Height:  |  Size: 911 B

View file

@ -278,6 +278,10 @@ void WindowScriptingInterface::makeConnection(bool success, const QString& userN
}
}
void WindowScriptingInterface::displayAnnouncement(const QString& message) {
emit announcement(message);
}
bool WindowScriptingInterface::isPhysicsEnabled() {
return qApp->isPhysicsEnabled();
}

View file

@ -58,6 +58,7 @@ public slots:
void copyToClipboard(const QString& text);
void takeSnapshot(bool notify = true, bool includeAnimated = false, float aspectRatio = 0.0f);
void makeConnection(bool success, const QString& userNameOrError);
void displayAnnouncement(const QString& message);
void shareSnapshot(const QString& path, const QUrl& href = QUrl(""));
bool isPhysicsEnabled();
@ -79,6 +80,7 @@ signals:
void connectionAdded(const QString& connectionName);
void connectionError(const QString& errorString);
void announcement(const QString& message);
void messageBoxClosed(int id, int button);

View file

@ -1,5 +1,6 @@
"use strict";
/*jslint vars:true, plusplus:true, forin:true*/
/*global Script, Settings, Window, Controller, Overlays, SoundArray, LODManager, MyAvatar, Tablet, Camera, HMD, Menu, Quat, Vec3*/
//
// notifications.js
// Version 0.801
@ -56,7 +57,6 @@
// }
// }
/* global Script, Controller, Overlays, SoundArray, Quat, Vec3, MyAvatar, Menu, HMD, AudioDevice, LODManager, Settings, Camera */
(function () { // BEGIN LOCAL_SCOPE
@ -77,8 +77,6 @@ var PERSIST_TIME_2D = 10.0; // Time in seconds before notification fades
var PERSIST_TIME_3D = 15.0;
var persistTime = PERSIST_TIME_2D;
var frame = 0;
var ourWidth = Window.innerWidth;
var ourHeight = Window.innerHeight;
var ctrlIsPressed = false;
var ready = true;
var MENU_NAME = 'Tools > Notifications';
@ -105,13 +103,14 @@ var NotificationType = {
{ text: "Connection" }
],
getTypeFromMenuItem: function (menuItemName) {
var type;
if (menuItemName.substr(menuItemName.length - NOTIFICATION_MENU_ITEM_POST.length) !== NOTIFICATION_MENU_ITEM_POST) {
return NotificationType.UNKNOWN;
}
var preMenuItemName = menuItemName.substr(0, menuItemName.length - NOTIFICATION_MENU_ITEM_POST.length);
for (var type in this.properties) {
for (type in this.properties) {
if (this.properties[type].text === preMenuItemName) {
return parseInt(type) + 1;
return parseInt(type, 10) + 1;
}
}
return NotificationType.UNKNOWN;
@ -123,8 +122,9 @@ var NotificationType = {
var randomSounds = new SoundArray({ localOnly: true }, true);
var numberOfSounds = 2;
for (var i = 1; i <= numberOfSounds; i++) {
randomSounds.addSound(Script.resolvePath("assets/sounds/notification-general"+ i + ".raw"));
var soundIndex;
for (soundIndex = 1; soundIndex <= numberOfSounds; soundIndex++) {
randomSounds.addSound(Script.resolvePath("assets/sounds/notification-general" + soundIndex + ".raw"));
}
var notifications = [];
@ -150,7 +150,7 @@ function createArrays(notice, button, createTime, height, myAlpha) {
// This handles the final dismissal of a notification after fading
function dismiss(firstNoteOut, firstButOut, firstOut) {
if (firstNoteOut == lodTextID) {
if (firstNoteOut === lodTextID) {
lodTextID = false;
}
@ -188,10 +188,10 @@ function fadeOut(noticeOut, buttonOut, arraysOut) {
pauseTimer = Script.setInterval(function () {
r -= 1;
rFade = r / 10.0;
rFade = Math.max(0.0, r / 10.0);
Overlays.editOverlay(noticeOut, { alpha: rFade });
Overlays.editOverlay(buttonOut, { alpha: rFade });
if (r < 0) {
if (r <= 0) {
dismiss(noticeOut, buttonOut, arraysOut);
arrays.splice(arraysOut, 1);
ready = true;
@ -290,32 +290,33 @@ function notify(notice, button, height, imageProperties, image) {
avatarOrientation,
notificationPosition,
notificationOrientation,
buttonPosition;
buttonPosition,
notificationIndex;
if (isOnHMD && notifications.length > 0) {
// Update 3D overlays to maintain positions relative to avatar
defaultEyePosition = MyAvatar.getDefaultEyePosition();
avatarOrientation = MyAvatar.orientation;
for (i = 0; i < notifications.length; i += 1) {
for (notificationIndex = 0; notificationIndex < notifications.length; notificationIndex += 1) {
notificationPosition = Vec3.sum(defaultEyePosition,
Vec3.multiplyQbyV(avatarOrientation,
overlay3DDetails[i].notificationPosition));
overlay3DDetails[notificationIndex].notificationPosition));
notificationOrientation = Quat.multiply(avatarOrientation,
overlay3DDetails[i].notificationOrientation);
overlay3DDetails[notificationIndex].notificationOrientation);
buttonPosition = Vec3.sum(defaultEyePosition,
Vec3.multiplyQbyV(avatarOrientation,
overlay3DDetails[i].buttonPosition));
Overlays.editOverlay(notifications[i], { position: notificationPosition,
overlay3DDetails[notificationIndex].buttonPosition));
Overlays.editOverlay(notifications[notificationIndex], { position: notificationPosition,
rotation: notificationOrientation });
Overlays.editOverlay(buttons[i], { position: buttonPosition, rotation: notificationOrientation });
Overlays.editOverlay(buttons[notificationIndex], { position: buttonPosition, rotation: notificationOrientation });
}
}
} else {
if (!image) {
notificationText = Overlays.addOverlay("text", notice);
notifications.push((notificationText));
notifications.push(notificationText);
} else {
notifications.push(Overlays.addOverlay("image", notice));
}
@ -413,7 +414,7 @@ function createNotification(text, notificationType, imageProperties) {
function deleteNotification(index) {
var notificationTextID = notifications[index];
if (notificationTextID == lodTextID) {
if (notificationTextID === lodTextID) {
lodTextID = false;
}
Overlays.deleteOverlay(notificationTextID);
@ -448,8 +449,7 @@ function wordWrap(string) {
}
function update() {
var nextOverlay,
noticeOut,
var noticeOut,
buttonOut,
arraysOut,
positions,
@ -470,7 +470,6 @@ function update() {
if ((frame % 60.0) === 0) { // only update once a second
locationY = 20.0;
for (i = 0; i < arrays.length; i += 1) { //repositions overlays as others fade
nextOverlay = Overlays.getOverlayAtPoint({ x: overlayLocationX, y: locationY });
Overlays.editOverlay(notifications[i], { x: overlayLocationX, y: locationY });
Overlays.editOverlay(buttons[i], { x: buttonLocationX, y: locationY + 12.0 });
if (isOnHMD) {
@ -593,6 +592,7 @@ function keyPressEvent(key) {
}
function setup() {
var type;
Menu.addMenu(MENU_NAME);
var checked = Settings.getValue(PLAY_NOTIFICATION_SOUNDS_SETTING);
checked = checked === '' ? true : checked;
@ -603,8 +603,8 @@ function setup() {
isChecked: Settings.getValue(PLAY_NOTIFICATION_SOUNDS_SETTING)
});
Menu.addSeparator(MENU_NAME, "Play sounds for:");
for (var type in NotificationType.properties) {
checked = Settings.getValue(PLAY_NOTIFICATION_SOUNDS_TYPE_SETTING_PRE + (parseInt(type) + 1));
for (type in NotificationType.properties) {
checked = Settings.getValue(PLAY_NOTIFICATION_SOUNDS_TYPE_SETTING_PRE + (parseInt(type, 10) + 1));
checked = checked === '' ? true : checked;
Menu.addMenuItem({
menuName: MENU_NAME,
@ -617,9 +617,10 @@ function setup() {
// When our script shuts down, we should clean up all of our overlays
function scriptEnding() {
for (var i = 0; i < notifications.length; i++) {
Overlays.deleteOverlay(notifications[i]);
Overlays.deleteOverlay(buttons[i]);
var notificationIndex;
for (notificationIndex = 0; notificationIndex < notifications.length; notificationIndex++) {
Overlays.deleteOverlay(notifications[notificationIndex]);
Overlays.deleteOverlay(buttons[notificationIndex]);
}
Menu.removeMenu(MENU_NAME);
}
@ -660,6 +661,7 @@ Window.stillSnapshotTaken.connect(onSnapshotTaken);
Window.processingGifStarted.connect(processingGif);
Window.connectionAdded.connect(connectionAdded);
Window.connectionError.connect(connectionError);
Window.announcement.connect(onNotify);
Window.notifyEditError = onEditError;
Window.notify = onNotify;
Tablet.tabletNotification.connect(tabletNotification);

View file

@ -1,4 +1,6 @@
"use strict";
/*jslint vars:true, plusplus:true, forin:true*/
/*global Window, Script, Tablet, HMD, Controller, Account, XMLHttpRequest, location, print*/
//
// goto.js
@ -16,6 +18,24 @@
var buttonName = "GOTO";
var onGotoScreen = false;
var shouldActivateButton = false;
function ignore() { }
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
var NORMAL_ICON = "icons/tablet-icons/goto-i.svg";
var NORMAL_ACTIVE = "icons/tablet-icons/goto-a.svg";
var WAITING_ICON = "icons/tablet-icons/goto-msg.svg";
var button = tablet.addButton({
icon: NORMAL_ICON,
activeIcon: NORMAL_ACTIVE,
text: buttonName,
sortOrder: 8
});
function messagesWaiting(isWaiting) {
button.editProperties({
icon: isWaiting ? WAITING_ICON : NORMAL_ICON
// No need for a different activeIcon, because we issue messagesWaiting(false) when the button goes active anyway.
});
}
function onClicked() {
if (onGotoScreen) {
@ -29,29 +49,115 @@
}
function onScreenChanged(type, url) {
ignore(type);
if (url === gotoQmlSource) {
onGotoScreen = true;
shouldActivateButton = true;
button.editProperties({isActive: shouldActivateButton});
messagesWaiting(false);
} else {
shouldActivateButton = false;
onGotoScreen = false;
button.editProperties({isActive: shouldActivateButton});
}
}
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
var button = tablet.addButton({
icon: "icons/tablet-icons/goto-i.svg",
activeIcon: "icons/tablet-icons/goto-a.svg",
text: buttonName,
sortOrder: 8
});
button.clicked.connect(onClicked);
tablet.screenChanged.connect(onScreenChanged);
function request(options, callback) { // cb(error, responseOfCorrectContentType) of url. A subset of npm request.
var httpRequest = new XMLHttpRequest(), key;
// QT bug: apparently doesn't handle onload. Workaround using readyState.
httpRequest.onreadystatechange = function () {
var READY_STATE_DONE = 4;
var HTTP_OK = 200;
if (httpRequest.readyState >= READY_STATE_DONE) {
var error = (httpRequest.status !== HTTP_OK) && httpRequest.status.toString() + ':' + httpRequest.statusText,
response = !error && httpRequest.responseText,
contentType = !error && httpRequest.getResponseHeader('content-type');
if (!error && contentType.indexOf('application/json') === 0) { // ignoring charset, etc.
try {
response = JSON.parse(response);
} catch (e) {
error = e;
}
}
callback(error, response);
}
};
if (typeof options === 'string') {
options = {uri: options};
}
if (options.url) {
options.uri = options.url;
}
if (!options.method) {
options.method = 'GET';
}
if (options.body && (options.method === 'GET')) { // add query parameters
var params = [], appender = (-1 === options.uri.search('?')) ? '?' : '&';
for (key in options.body) {
params.push(key + '=' + options.body[key]);
}
options.uri += appender + params.join('&');
delete options.body;
}
if (options.json) {
options.headers = options.headers || {};
options.headers["Content-type"] = "application/json";
options.body = JSON.stringify(options.body);
}
for (key in options.headers || {}) {
httpRequest.setRequestHeader(key, options.headers[key]);
}
httpRequest.open(options.method, options.uri, true);
httpRequest.send(options.body);
}
var stories = {};
var DEBUG = false;
function pollForAnnouncements() {
var actions = DEBUG ? 'snapshot' : 'announcement';
var count = DEBUG ? 10 : 100;
var options = [
'now=' + new Date().toISOString(),
'include_actions=' + actions,
'restriction=' + (Account.isLoggedIn() ? 'open,hifi' : 'open'),
'require_online=true',
'protocol=' + encodeURIComponent(location.protocolVersion()),
'per_page=' + count
];
var url = location.metaverseServerUrl + '/api/v1/user_stories?' + options.join('&');
request({
uri: url
}, function (error, data) {
if (error || (data.status !== 'success')) {
print("Error: unable to get", url, error || data.status);
return;
}
var didNotify = false;
data.user_stories.forEach(function (story) {
if (stories[story.id]) { // already seen
return;
}
stories[story.id] = story;
var message = story.username + " says something is happening in " + story.place_name + ". Open GOTO to join them.";
Window.displayAnnouncement(message);
didNotify = true;
});
if (didNotify) {
messagesWaiting(true);
if (HMD.isHandControllerAvailable()) {
var STRENGTH = 1.0, DURATION_MS = 60, HAND = 2; // both hands
Controller.triggerHapticPulse(STRENGTH, DURATION_MS, HAND);
}
}
});
}
var ANNOUNCEMENTS_POLL_TIME_MS = (DEBUG ? 10 : 60) * 1000;
var pollTimer = Script.setInterval(pollForAnnouncements, ANNOUNCEMENTS_POLL_TIME_MS);
Script.scriptEnding.connect(function () {
Script.clearInterval(pollTimer);
button.clicked.disconnect(onClicked);
tablet.removeButton(button);
tablet.screenChanged.disconnect(onScreenChanged);