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