diff --git a/AppApi.cs b/AppApi.cs
index 229c2a84..93180c00 100644
--- a/AppApi.cs
+++ b/AppApi.cs
@@ -17,6 +17,8 @@ using System.Net;
using Windows.UI.Notifications;
using Windows.Data.Xml.Dom;
using librsync.net;
+using XSNotifications;
+using XSNotifications.Enum;
namespace VRCX
{
@@ -208,22 +210,33 @@ namespace VRCX
return CpuMonitor.Instance.CpuUsage;
}
- public void DesktopNotification(string BoldText, string Text, string ImageURL = "")
+ public bool CacheImage(string ImageURL, string AppVersion)
{
- XmlDocument toastXml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastImageAndText02);
- XmlNodeList stringElements = toastXml.GetElementsByTagName("text");
- String imagePath = Path.Combine(Program.BaseDirectory, "cache\\toast");
- if (ImageURL == String.Empty)
- {
- imagePath = Path.Combine(Program.BaseDirectory, "VRCX.ico");
- }
- else
+ String Icon = Path.Combine(Program.BaseDirectory, "cache\\toast");
+ try
{
using (var client = new WebClient())
{
- client.DownloadFile(ImageURL, imagePath);
+ client.Headers.Add("user-agent", AppVersion);
+ client.DownloadFile(ImageURL, Icon);
}
}
+ catch (WebException)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ public void DesktopNotification(string BoldText, string Text, bool Image)
+ {
+ XmlDocument toastXml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastImageAndText02);
+ XmlNodeList stringElements = toastXml.GetElementsByTagName("text");
+ String imagePath = Path.Combine(Program.BaseDirectory, "VRCX.ico");
+ if (Image)
+ {
+ imagePath = Path.Combine(Program.BaseDirectory, "cache\\toast");
+ }
stringElements[0].AppendChild(toastXml.CreateTextNode(BoldText));
stringElements[1].AppendChild(toastXml.CreateTextNode(Text));
XmlNodeList imageElements = toastXml.GetElementsByTagName("image");
@@ -232,6 +245,28 @@ namespace VRCX
ToastNotificationManager.CreateToastNotifier("VRCX").Show(toast);
}
+ public void XSNotification(string Title, string Content, int Timeout, bool Image)
+ {
+ bool UseBase64Icon = true;
+ String Icon = "iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAAsTAAALEwEAmpwYAAAHaGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDIxLTA0LTA4VDE0OjU3OjAxKzEyOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAyMS0wNC0wOFQxNjozMzoxMCsxMjowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyMS0wNC0wOFQxNjozMzoxMCsxMjowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo2YTY5MmQzYi03ZTJkLTNiNGUtYTMzZC1hN2MwOTNlOGU0OTkiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDo1NTE2MWIyMi1hYzgxLTY3NDYtODAyYi1kODIzYWFmN2RjYjciIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo3ZjJjNTA2ZS02YTVhLWRhNGEtOTg5Mi02NDZiMzQ0MGQxZTgiPiA8cGhvdG9zaG9wOkRvY3VtZW50QW5jZXN0b3JzPiA8cmRmOkJhZz4gPHJkZjpsaT5hZG9iZTpkb2NpZDpwaG90b3Nob3A6NmJmOGE5MTgtY2QzZS03OTRjLTk3NzktMzM0YjYwZWJiNTYyPC9yZGY6bGk+IDwvcmRmOkJhZz4gPC9waG90b3Nob3A6RG9jdW1lbnRBbmNlc3RvcnM+IDx4bXBNTTpIaXN0b3J5PiA8cmRmOlNlcT4gPHJkZjpsaSBzdEV2dDphY3Rpb249ImNyZWF0ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6N2YyYzUwNmUtNmE1YS1kYTRhLTk4OTItNjQ2YjM0NDBkMWU4IiBzdEV2dDp3aGVuPSIyMDIxLTA0LTA4VDE0OjU3OjAxKzEyOjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOmJhM2ZjODI3LTM0ZjQtYjU0OC05ZGFiLTZhMTZlZmQzZjAxMSIgc3RFdnQ6d2hlbj0iMjAyMS0wNC0wOFQxNTowMTozMSsxMjowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDo2YTY5MmQzYi03ZTJkLTNiNGUtYTMzZC1hN2MwOTNlOGU0OTkiIHN0RXZ0OndoZW49IjIwMjEtMDQtMDhUMTY6MzM6MTArMTI6MDAiIHN0RXZ0OnNv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";
+ if (Image)
+ {
+ UseBase64Icon = false;
+ Icon = Path.Combine(Program.BaseDirectory, "cache\\toast");
+ }
+ new XSNotifier().SendNotification(new XSNotification()
+ {
+ Height = 110.0f,
+ SourceApp = "VRCX",
+ AudioPath = "",
+ Title = Title,
+ Content = Content,
+ Timeout = Timeout,
+ UseBase64Icon = UseBase64Icon,
+ Icon = Icon
+ });
+ }
+
public void SetStartup(bool enabled)
{
try
diff --git a/VRCX.csproj b/VRCX.csproj
index 2a37b891..74924a87 100644
--- a/VRCX.csproj
+++ b/VRCX.csproj
@@ -209,6 +209,9 @@
1.0.113.7
+
+ 0.1.1
+
\ No newline at end of file
diff --git a/VRCXVR.cs b/VRCXVR.cs
index 9f182775..a8e38694 100644
--- a/VRCXVR.cs
+++ b/VRCXVR.cs
@@ -136,9 +136,14 @@ namespace VRCX
while (_thread != null)
{
- _browser1.RenderToTexture(_texture1);
- _browser2.RenderToTexture(_texture2);
-
+ if ("true".Equals(SharedVariable.Instance.Get("config:vrcx_overlaywrist")))
+ {
+ _browser1.RenderToTexture(_texture1);
+ }
+ if ("true".Equals(SharedVariable.Instance.Get("config:vrcx_overlaynotifications")))
+ {
+ _browser2.RenderToTexture(_texture2);
+ }
try
{
Thread.Sleep(16);
diff --git a/html/src/app.js b/html/src/app.js
index 6c9474c2..b2e19ea2 100644
--- a/html/src/app.js
+++ b/html/src/app.js
@@ -4242,14 +4242,60 @@ speechSynthesis.getVoices();
if ((this.notificationTTS) && (this.isGameRunning)) {
this.playNotyTTS(noty, message);
}
+ var imageURL = await this.notyGetImage(noty);
+ var image = false;
+ if ((imageURL) &&
+ (((this.desktopToast === 'Always') ||
+ ((this.desktopToast === 'Game Closed') && (!this.isGameRunning)) ||
+ ((this.desktopToast === 'Desktop Mode') && (this.isGameNoVR) && (this.isGameRunning))) ||
+ ((this.xsNotifications) && (this.isGameRunning) && (!this.isGameNoVR)))) {
+ var image = await AppApi.CacheImage(imageURL, appVersion);
+ }
+ if ((this.xsNotifications) && (this.isGameRunning) && (!this.isGameNoVR)) {
+ this.displayXSNotification(noty, message, image);
+ }
if ((this.desktopToast === 'Always') ||
((this.desktopToast === 'Game Closed') && (!this.isGameRunning)) ||
((this.desktopToast === 'Desktop Mode') && (this.isGameNoVR) && (this.isGameRunning))) {
- this.displayDesktopToast(noty, message);
+ this.displayDesktopToast(noty, message, image);
}
}
};
+ $app.methods.notyGetImage = async function (noty) {
+ var imageURL = '';
+ var userId = '';
+ if (noty.userId) {
+ userId = noty.userId;
+ } else if (noty.senderUserId) {
+ userId = noty.senderUserId;
+ } else if (noty.sourceUserId) {
+ userId = noty.sourceUserId;
+ } else if (noty.data) {
+ for (var ref of API.cachedUsers.values()) {
+ if (ref.displayName === noty.data) {
+ userId = ref.id;
+ break;
+ }
+ }
+ }
+ if ((noty.details) && (noty.details.imageUrl)) {
+ imageURL = noty.details.imageUrl;
+ } else if (userId) {
+ imageURL = await API.getCachedUser({
+ userId: userId
+ }).catch((err) => {
+ throw err;
+ }).then((args) => {
+ if ((this.displayVRCPlusIconsAsAvatar) && (args.json.userIcon)) {
+ return args.json.userIcon;
+ }
+ return args.json.currentAvatarThumbnailImageUrl;
+ });
+ }
+ return imageURL;
+ };
+
$app.methods.playNotyTTS = async function (noty, message) {
switch (noty.type) {
case 'OnPlayerJoined':
@@ -4320,100 +4366,141 @@ speechSynthesis.getVoices();
}
};
- $app.methods.displayDesktopToast = async function (noty, message) {
- var imageURL = '';
- var userId = '';
- if (noty.userId) {
- userId = noty.userId;
- } else if (noty.senderUserId) {
- userId = noty.senderUserId;
- } else if (noty.sourceUserId) {
- userId = noty.sourceUserId;
- } else if (noty.data) {
- for (var ref of API.cachedUsers.values()) {
- if (ref.displayName === noty.data) {
- userId = ref.id;
- break;
- }
- }
- }
- if ((noty.details) && (noty.details.imageUrl)) {
- imageURL = noty.details.imageUrl;
- } else if (userId) {
- imageURL = await API.getCachedUser({
- userId: userId
- }).catch((err) => {
- throw err;
- }).then((args) => {
- if ((this.displayVRCPlusIconsAsAvatar) && (args.json.userIcon)) {
- return args.json.userIcon;
- }
- return args.json.currentAvatarThumbnailImageUrl;
- });
- }
+ $app.methods.displayXSNotification = async function (noty, message, image) {
+ var timeout = parseInt(this.notificationTimeout) / 1000;
switch (noty.type) {
case 'OnPlayerJoined':
- AppApi.DesktopNotification(noty.data, 'has joined', imageURL);
+ AppApi.XSNotification('VRCX', `${noty.data} has joined`, timeout, image);
break;
case 'OnPlayerLeft':
- AppApi.DesktopNotification(noty.data, 'has left', imageURL);
+ AppApi.XSNotification('VRCX', `${noty.data} has left`, timeout, image);
break;
case 'OnPlayerJoining':
- AppApi.DesktopNotification(noty.displayName, 'is joining', imageURL);
+ AppApi.XSNotification('VRCX', `${noty.displayName} is joining`, timeout, image);
break;
case 'GPS':
- AppApi.DesktopNotification(noty.displayName, `is in ${await this.displayLocation(noty.location[0])}`, imageURL);
+ AppApi.XSNotification('VRCX', `${noty.displayName} is in ${await this.displayLocation(noty.location[0])}`, timeout, image);
break;
case 'Online':
- AppApi.DesktopNotification(noty.displayName, 'has logged in', imageURL);
+ AppApi.XSNotification('VRCX', `${noty.displayName} has logged in`, timeout, image);
break;
case 'Offline':
- AppApi.DesktopNotification(noty.displayName, 'has logged out', imageURL);
+ AppApi.XSNotification('VRCX', `${noty.displayName} has logged out`, timeout, image);
break;
case 'Status':
- AppApi.DesktopNotification(noty.displayName, `status is now ${noty.status[0].status} ${noty.status[0].statusDescription}`, imageURL);
+ AppApi.XSNotification('VRCX', `${noty.displayName} status is now ${noty.status[0].status} ${noty.status[0].statusDescription}`, timeout, image);
break;
case 'invite':
- AppApi.DesktopNotification(noty.senderUsername, `has invited you to ${noty.details.worldName}${message}`, imageURL);
+ AppApi.XSNotification('VRCX', `${noty.senderUsername} has invited you to ${noty.details.worldName}${message}`, timeout, image);
break;
case 'requestInvite':
- AppApi.DesktopNotification(noty.senderUsername, `has requested an invite${message}`, imageURL);
+ AppApi.XSNotification('VRCX', `${noty.senderUsername} has requested an invite${message}`, timeout, image);
break;
case 'inviteResponse':
- AppApi.DesktopNotification(noty.senderUsername, `has responded to your invite${message}`, imageURL);
+ AppApi.XSNotification('VRCX', `${noty.senderUsername} has responded to your invite${message}`, timeout, image);
break;
case 'requestInviteResponse':
- AppApi.DesktopNotification(noty.senderUsername, `has responded to your invite request${message}`, imageURL);
+ AppApi.XSNotification('VRCX', `${noty.senderUsername} has responded to your invite request${message}`, timeout, image);
break;
case 'friendRequest':
- AppApi.DesktopNotification(noty.senderUsername, 'has sent you a friend request', imageURL);
+ AppApi.XSNotification('VRCX', `${noty.senderUsername} has sent you a friend request`, timeout, image);
break;
case 'Friend':
- AppApi.DesktopNotification(noty.displayName, 'is now your friend', imageURL);
+ AppApi.XSNotification('VRCX', `${noty.displayName} is now your friend`, timeout, image);
break;
case 'Unfriend':
- AppApi.DesktopNotification(noty.displayName, 'is no longer your friend', imageURL);
+ AppApi.XSNotification('VRCX', `${noty.displayName} is no longer your friend`, timeout, image);
break;
case 'TrustLevel':
- AppApi.DesktopNotification(noty.displayName, `trust level is now ${noty.trustLevel}`, imageURL);
+ AppApi.XSNotification('VRCX', `${noty.displayName} trust level is now ${noty.trustLevel}`, timeout, image);
break;
case 'DisplayName':
- AppApi.DesktopNotification(noty.previousDisplayName, `changed their name to ${noty.displayName}`, imageURL);
+ AppApi.XSNotification('VRCX', `${noty.previousDisplayName} changed their name to ${noty.displayName}`, timeout, image);
break;
case 'showAvatar':
- AppApi.DesktopNotification(noty.sourceDisplayName, `has shown your avatar`, imageURL);
+ AppApi.XSNotification('VRCX', `${noty.sourceDisplayName} has shown your avatar`, timeout, image);
break;
case 'hideAvatar':
- AppApi.DesktopNotification(noty.sourceDisplayName, `has hidden your avatar`, imageURL);
+ AppApi.XSNotification('VRCX', `${noty.sourceDisplayName} has hidden your avatar`, timeout, image);
break;
case 'block':
- AppApi.DesktopNotification(noty.sourceDisplayName, `has blocked you`, imageURL);
+ AppApi.XSNotification('VRCX', `${noty.sourceDisplayName} has blocked you`, timeout, image);
break;
case 'mute':
- AppApi.DesktopNotification(noty.sourceDisplayName, `has muted you`, imageURL);
+ AppApi.XSNotification('VRCX', `${noty.sourceDisplayName} has muted you`, timeout, image);
break;
case 'unmute':
- AppApi.DesktopNotification(noty.sourceDisplayName, `has unmuted you`, imageURL);
+ AppApi.XSNotification('VRCX', `${noty.sourceDisplayName} has unmuted you`, timeout, image);
+ break;
+ default:
+ break;
+ }
+ };
+
+ $app.methods.displayDesktopToast = async function (noty, message, image) {
+ switch (noty.type) {
+ case 'OnPlayerJoined':
+ AppApi.DesktopNotification(noty.data, 'has joined', image);
+ break;
+ case 'OnPlayerLeft':
+ AppApi.DesktopNotification(noty.data, 'has left', image);
+ break;
+ case 'OnPlayerJoining':
+ AppApi.DesktopNotification(noty.displayName, 'is joining', image);
+ break;
+ case 'GPS':
+ AppApi.DesktopNotification(noty.displayName, `is in ${await this.displayLocation(noty.location[0])}`, image);
+ break;
+ case 'Online':
+ AppApi.DesktopNotification(noty.displayName, 'has logged in', image);
+ break;
+ case 'Offline':
+ AppApi.DesktopNotification(noty.displayName, 'has logged out', image);
+ break;
+ case 'Status':
+ AppApi.DesktopNotification(noty.displayName, `status is now ${noty.status[0].status} ${noty.status[0].statusDescription}`, image);
+ break;
+ case 'invite':
+ AppApi.DesktopNotification(noty.senderUsername, `has invited you to ${noty.details.worldName}${message}`, image);
+ break;
+ case 'requestInvite':
+ AppApi.DesktopNotification(noty.senderUsername, `has requested an invite${message}`, image);
+ break;
+ case 'inviteResponse':
+ AppApi.DesktopNotification(noty.senderUsername, `has responded to your invite${message}`, image);
+ break;
+ case 'requestInviteResponse':
+ AppApi.DesktopNotification(noty.senderUsername, `has responded to your invite request${message}`, image);
+ break;
+ case 'friendRequest':
+ AppApi.DesktopNotification(noty.senderUsername, 'has sent you a friend request', image);
+ break;
+ case 'Friend':
+ AppApi.DesktopNotification(noty.displayName, 'is now your friend', image);
+ break;
+ case 'Unfriend':
+ AppApi.DesktopNotification(noty.displayName, 'is no longer your friend', image);
+ break;
+ case 'TrustLevel':
+ AppApi.DesktopNotification(noty.displayName, `trust level is now ${noty.trustLevel}`, image);
+ break;
+ case 'DisplayName':
+ AppApi.DesktopNotification(noty.previousDisplayName, `changed their name to ${noty.displayName}`, image);
+ break;
+ case 'showAvatar':
+ AppApi.DesktopNotification(noty.sourceDisplayName, `has shown your avatar`, image);
+ break;
+ case 'hideAvatar':
+ AppApi.DesktopNotification(noty.sourceDisplayName, `has hidden your avatar`, image);
+ break;
+ case 'block':
+ AppApi.DesktopNotification(noty.sourceDisplayName, `has blocked you`, image);
+ break;
+ case 'mute':
+ AppApi.DesktopNotification(noty.sourceDisplayName, `has muted you`, image);
+ break;
+ case 'unmute':
+ AppApi.DesktopNotification(noty.sourceDisplayName, `has unmuted you`, image);
break;
default:
break;
@@ -6710,6 +6797,8 @@ speechSynthesis.getVoices();
$app.data.hideOnPlayerJoined = configRepository.getBool('VRCX_hideOnPlayerJoined');
$app.data.hideDevicesFromFeed = configRepository.getBool('VRCX_hideDevicesFromFeed');
$app.data.overlayNotifications = configRepository.getBool('VRCX_overlayNotifications');
+ $app.data.overlayWrist = configRepository.getBool('VRCX_overlayWrist');
+ $app.data.xsNotifications = configRepository.getBool('VRCX_xsNotifications');
$app.data.desktopToast = configRepository.getString('VRCX_desktopToast');
$app.data.minimalFeed = configRepository.getBool('VRCX_minimalFeed');
$app.data.displayVRCPlusIconsAsAvatar = configRepository.getBool('displayVRCPlusIconsAsAvatar');
@@ -6717,6 +6806,9 @@ speechSynthesis.getVoices();
$app.data.notificationTTSVoice = configRepository.getString('VRCX_notificationTTSVoice');
$app.data.notificationTimeout = configRepository.getString('VRCX_notificationTimeout');
var saveOpenVROption = function () {
+ if ((this.openVR) && (!this.overlayNotifications) && (!this.overlayWrist)) {
+ this.openVR = false;
+ }
configRepository.setBool('openVR', this.openVR);
configRepository.setBool('openVRAlways', this.openVRAlways);
configRepository.setBool('VRCX_overlaybutton', this.overlaybutton);
@@ -6724,6 +6816,8 @@ speechSynthesis.getVoices();
configRepository.setBool('VRCX_hideOnPlayerJoined', this.hideOnPlayerJoined);
configRepository.setBool('VRCX_hideDevicesFromFeed', this.hideDevicesFromFeed);
configRepository.setBool('VRCX_overlayNotifications', this.overlayNotifications);
+ configRepository.setBool('VRCX_overlayWrist', this.overlayWrist);
+ configRepository.setBool('VRCX_xsNotifications', this.xsNotifications);
configRepository.setString('VRCX_desktopToast', this.desktopToast);
configRepository.setBool('VRCX_minimalFeed', this.minimalFeed);
configRepository.setBool('displayVRCPlusIconsAsAvatar', this.displayVRCPlusIconsAsAvatar);
@@ -6745,6 +6839,8 @@ speechSynthesis.getVoices();
$app.watch.hideOnPlayerJoined = saveOpenVROption;
$app.watch.hideDevicesFromFeed = saveOpenVROption;
$app.watch.overlayNotifications = saveOpenVROption;
+ $app.watch.overlayWrist = saveOpenVROption;
+ $app.watch.xsNotifications = saveOpenVROption;
$app.watch.desktopToast = saveOpenVROption;
$app.watch.minimalFeed = saveOpenVROption;
$app.watch.displayVRCPlusIconsAsAvatar = saveOpenVROption;
@@ -7016,7 +7112,6 @@ speechSynthesis.getVoices();
notificationTTS: this.notificationTTS,
notificationTTSVoice: this.notificationTTSVoice,
overlayNotifications: this.overlayNotifications,
- desktopToast: this.desktopToast,
hideDevicesFromFeed: this.hideDevicesFromFeed,
minimalFeed: this.minimalFeed,
displayVRCPlusIconsAsAvatar: this.displayVRCPlusIconsAsAvatar,
diff --git a/html/src/index.pug b/html/src/index.pug
index b44bd14f..c3b2244c 100644
--- a/html/src/index.pug
+++ b/html/src/index.pug
@@ -747,40 +747,48 @@ html
div.options-container-item
span.name Force Run (Opens SteamVR)
el-switch(v-model="openVRAlways" :disabled="!openVR")
- div.options-container-item
- span.name(style="min-width:137px") Overlay Button
- el-switch(v-model="overlaybutton" inactive-text="Grip" active-text="Menu" :disabled="!openVR")
br
span.sub-header Display Options
- div.options-container-item
- span.name Minimal Feed Icons
- el-switch(v-model="minimalFeed" :disabled="!openVR")
- div.options-container-item
- span.name Hide VR Devices
- el-switch(v-model="hideDevicesFromFeed" :disabled="!openVR")
div.options-container-item
span.name Hide Private Worlds
el-switch(v-model="hidePrivateFromFeed")
div.options-container-item
span.name Hide Joins When Joining
el-switch(v-model="hideOnPlayerJoined")
- div.options-container-item
- el-button(size="small" icon="el-icon-notebook-2" @click="showWristFeedFiltersDialog()" :disabled="!openVR") Wrist Feed Filters
- el-button(size="small" icon="el-icon-chat-square" @click="showNotyFeedFiltersDialog()") Notification Filters
br
- span.sub-header VR Notifications
+ span.sub-header Wrist Feed
+ div.options-container-item
+ span.name Wrist Feed Overlay
+ el-switch(v-model="overlayWrist" :disabled="!openVR")
+ div.options-container-item
+ span.name(style="min-width:137px") Overlay Button
+ el-switch(v-model="overlaybutton" inactive-text="Grip" active-text="Menu" :disabled="!openVR || !overlayWrist")
+ div.options-container-item
+ span.name Minimal Feed Icons
+ el-switch(v-model="minimalFeed" :disabled="!openVR || !overlayWrist")
+ div.options-container-item
+ span.name Hide VR Devices
+ el-switch(v-model="hideDevicesFromFeed" :disabled="!openVR || !overlayWrist")
+ div.options-container-item
+ el-button(size="small" icon="el-icon-notebook-2" @click="showWristFeedFiltersDialog()" :disabled="!openVR || !overlayWrist") Wrist Feed Filters
+ br
+ span.sub-header Notifications
div.options-container-item
span.name Overlay Notifications
el-switch(v-model="overlayNotifications" :disabled="!openVR")
div.options-container-item
- el-button(size="small" icon="el-icon-time" @click="promptNotificationTimeout()" :disabled="!overlayNotifications || !openVR") Notification Timeout
el-button(size="small" icon="el-icon-rank" @click="showNotificationPositionDialog()" :disabled="!overlayNotifications || !openVR") Notification Position
- br
- span.sub-header Desktop Notifications
div.options-container-item
- span.name When to display notifications:
+ span.name XSOverlay Notifications
+ el-switch(v-model="xsNotifications")
+ div.options-container-item
+ el-button(size="small" icon="el-icon-time" @click="promptNotificationTimeout()" :disabled="(!overlayNotifications || !openVR) && !xsNotifications") Notification Timeout
+ div.options-container-item
+ span.name Desktop Notifications, When to display:
br
toggle-switch(:options="desktopToastToggleSwitchOption" group="desktopToastToggleSwitchOption" v-model="desktopToast" class="toggle-switch")
+ div.options-container-item
+ el-button(size="small" icon="el-icon-chat-square" @click="showNotyFeedFiltersDialog()") Notification Filters
br
span.sub-header TTS Options
div.options-container-item