diff --git a/html/package-lock.json b/html/package-lock.json
index e67512f2..570f155b 100644
--- a/html/package-lock.json
+++ b/html/package-lock.json
@@ -12394,6 +12394,11 @@
"integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==",
"dev": true
},
+ "vuejs-toggle-switch": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/vuejs-toggle-switch/-/vuejs-toggle-switch-1.3.2.tgz",
+ "integrity": "sha512-HkSB0hBWgol/o2LY9c1E/f4x+pE09uzlKAYs8bdmHexyudFO1/O1xFJA9M9aDe8DpIAXNxrZCwNwIZdCV97Sdw=="
+ },
"watchpack": {
"version": "1.7.5",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz",
diff --git a/html/package.json b/html/package.json
index ce9c978b..6ae99df8 100644
--- a/html/package.json
+++ b/html/package.json
@@ -45,6 +45,7 @@
"stylelint-scss": "^3.18.0",
"vue": "^2.6.12",
"vue-data-tables": "^3.4.5",
- "vue-lazyload": "^1.3.3"
+ "vue-lazyload": "^1.3.3",
+ "vuejs-toggle-switch": "^1.3.2"
}
}
diff --git a/html/src/app.js b/html/src/app.js
index 719d9dd9..d1b8e50d 100644
--- a/html/src/app.js
+++ b/html/src/app.js
@@ -8,6 +8,7 @@ import Noty from 'noty';
import Vue from 'vue';
import VueLazyload from 'vue-lazyload';
import { DataTables } from 'vue-data-tables';
+import ToggleSwitch from 'vuejs-toggle-switch'
import ElementUI from 'element-ui';
import locale from 'element-ui/lib/locale/lang/en';
@@ -3415,7 +3416,6 @@ speechSynthesis.getVoices();
AppApi.CheckGameRunning().then(([isGameRunning, isGameNoVR]) => {
if (isGameRunning !== this.isGameRunning) {
this.isGameRunning = isGameRunning;
- sharedRepository.setBool('is_game_running', isGameRunning);
Discord.SetTimestamps(Date.now(), 0);
}
this.isGameNoVR = isGameNoVR;
@@ -3472,9 +3472,7 @@ speechSynthesis.getVoices();
}
var ctx = data[--i];
// GPS, Online, Offline, Status, Avatar
- if ((ctx.type !== 'Avatar') &&
- !((ctx.type === 'GPS') && (ctx.location[0] === 'private') && (this.hidePrivateFromFeed === true)) &&
- !(((ctx.type === 'Online') || (ctx.type === 'Offline')) && (this.hideLoginsFromFeed === true))) {
+ if (ctx.type !== 'Avatar') {
arr.push({
...ctx,
isFriend: this.friends.has(ctx.userId),
@@ -3483,6 +3481,7 @@ speechSynthesis.getVoices();
++j;
}
}
+ // invite, requestInvite, friendRequest
var { data } = this.notificationTable;
for (i = 0; i < data.length; i++) {
var ctx = data[i];
@@ -5705,7 +5704,6 @@ speechSynthesis.getVoices();
$app.data.openVRAlways = configRepository.getBool('openVRAlways');
$app.data.overlaybutton = configRepository.getBool('VRCX_overlaybutton');
$app.data.hidePrivateFromFeed = configRepository.getBool('VRCX_hidePrivateFromFeed');
- $app.data.hideLoginsFromFeed = configRepository.getBool('VRCX_hideLoginsFromFeed');
$app.data.hideDevicesFromFeed = configRepository.getBool('VRCX_hideDevicesFromFeed');
$app.data.overlayNotifications = configRepository.getBool('VRCX_overlayNotifications');
$app.data.minimalFeed = configRepository.getBool('VRCX_minimalFeed');
@@ -5717,10 +5715,10 @@ speechSynthesis.getVoices();
configRepository.setBool('openVRAlways', this.openVRAlways);
configRepository.setBool('VRCX_overlaybutton', this.overlaybutton);
configRepository.setBool('VRCX_hidePrivateFromFeed', this.hidePrivateFromFeed);
- configRepository.setBool('VRCX_hideLoginsFromFeed', this.hideLoginsFromFeed);
configRepository.setBool('VRCX_hideDevicesFromFeed', this.hideDevicesFromFeed);
configRepository.setBool('VRCX_overlayNotifications', this.overlayNotifications);
configRepository.setBool('VRCX_minimalFeed', this.minimalFeed);
+ AppApi.RefreshVR();
};
$app.data.TTSvoices = speechSynthesis.getVoices();
var saveNotificationTTS = function () {
@@ -5729,12 +5727,12 @@ speechSynthesis.getVoices();
if (this.notificationTTS) {
this.speak('Notification text-to-speech enabled');
}
+ AppApi.RefreshVR();
};
$app.watch.openVR = saveOpenVROption;
$app.watch.openVRAlways = saveOpenVROption;
$app.watch.overlaybutton = saveOpenVROption;
$app.watch.hidePrivateFromFeed = saveOpenVROption;
- $app.watch.hideLoginsFromFeed = saveOpenVROption;
$app.watch.hideDevicesFromFeed = saveOpenVROption;
$app.watch.overlayNotifications = saveOpenVROption;
$app.watch.minimalFeed = saveOpenVROption;
@@ -5744,6 +5742,7 @@ speechSynthesis.getVoices();
$app.watch.isDarkMode = function () {
configRepository.setBool('isDarkMode', this.isDarkMode);
$appDarkStyle.disabled = this.isDarkMode === false;
+ AppApi.RefreshVR();
};
$app.data.isStartAtWindowsStartup = configRepository.getBool('VRCX_StartAtWindowsStartup');
$app.data.isStartAsMinimizedState = (VRCXStorage.Get('VRCX_StartAsMinimizedState') === 'true');
@@ -5760,40 +5759,142 @@ speechSynthesis.getVoices();
$app.watch.isStartAsMinimizedState = saveVRCXWindowOption;
$app.watch.isCloseToTray = saveVRCXWindowOption;
$app.watch.isAutoLogin = saveVRCXWindowOption;
- if (!configRepository.getString('VRCX_notificationTimeout')) {
- $app.data.notificationTimeout = 3000;
- configRepository.setString('VRCX_notificationTimeout', $app.data.notificationTimeout);
- }
- if (!configRepository.getString('VRCX_notificationJoinLeaveFilter')) {
- $app.data.notificationJoinLeaveFilter = 'VIP';
- configRepository.setString('VRCX_notificationJoinLeaveFilter', $app.data.notificationJoinLeaveFilter);
- }
- if (!configRepository.getString('VRCX_notificationOnlineOfflineFilter')) {
- $app.data.notificationOnlineOfflineFilter = 'VIP';
- configRepository.setString('VRCX_notificationOnlineOfflineFilter', $app.data.notificationOnlineOfflineFilter);
- }
+
+ //setting defaults
if (!configRepository.getString('VRCX_notificationPosition')) {
$app.data.notificationPosition = 'topCenter';
configRepository.setString('VRCX_notificationPosition', $app.data.notificationPosition);
}
+ if (!configRepository.getString('VRCX_notificationTimeout')) {
+ $app.data.notificationTimeout = 3000;
+ configRepository.setString('VRCX_notificationTimeout', $app.data.notificationTimeout);
+ }
if (!configRepository.getString('VRCX_notificationTTSVoice')) {
$app.data.notificationTTSVoice = '0';
configRepository.setString('VRCX_notificationTTSVoice', $app.data.notificationTTSVoice);
}
- $app.data.notificationJoinLeaveFilter = configRepository.getString('VRCX_notificationJoinLeaveFilter');
- $app.methods.changeNotificationJoinLeaveFilter = function () {
- configRepository.setString('VRCX_notificationJoinLeaveFilter', this.notificationJoinLeaveFilter);
+ if (!configRepository.getString('sharedFeedFilters')) {
+ var sharedFeedFilters = {
+ noty: {},
+ wrist: {}
+ };
+ sharedFeedFilters.noty.Location = 'Off';
+ sharedFeedFilters.noty.OnPlayerJoined = 'VIP';
+ sharedFeedFilters.noty.OnPlayerLeft = 'VIP';
+ sharedFeedFilters.noty.OnPlayerJoining = 'VIP';
+ sharedFeedFilters.noty.Online = 'VIP';
+ sharedFeedFilters.noty.Offline = 'VIP';
+ sharedFeedFilters.noty.GPS = 'Off';
+ sharedFeedFilters.noty.Status = 'Off';
+ sharedFeedFilters.noty.invite = 'Friends';
+ sharedFeedFilters.noty.requestInvite = 'Friends';
+ sharedFeedFilters.noty.friendRequest = 'On';
+ sharedFeedFilters.noty.Friend = 'On';
+ sharedFeedFilters.noty.Unfriend = 'On';
+ sharedFeedFilters.noty.DisplayName = 'VIP';
+ sharedFeedFilters.noty.TrustLevel = 'VIP';
+ sharedFeedFilters.wrist.Location = 'On';
+ sharedFeedFilters.wrist.OnPlayerJoined = 'Everyone';
+ sharedFeedFilters.wrist.OnPlayerLeft = 'Everyone';
+ sharedFeedFilters.wrist.OnPlayerJoining = 'Friends';
+ sharedFeedFilters.wrist.Online = 'Friends';
+ sharedFeedFilters.wrist.Offline = 'Friends';
+ sharedFeedFilters.wrist.GPS = 'Friends';
+ sharedFeedFilters.wrist.Status = 'Friends';
+ sharedFeedFilters.wrist.invite = 'Friends';
+ sharedFeedFilters.wrist.requestInvite = 'Friends';
+ sharedFeedFilters.wrist.friendRequest = 'On';
+ sharedFeedFilters.wrist.Friend = 'On';
+ sharedFeedFilters.wrist.Unfriend = 'On';
+ sharedFeedFilters.wrist.DisplayName = 'Friends';
+ sharedFeedFilters.wrist.TrustLevel = 'Friends';
+ configRepository.setString('sharedFeedFilters', JSON.stringify(sharedFeedFilters));
+ }
+ $app.data.sharedFeedFilters = JSON.parse(configRepository.getString('sharedFeedFilters'));
+
+ $app.data.toggleSwitchOptionsEveryone = {
+ layout: {
+ backgroundColor: "white",
+ selectedBackgroundColor: "#409eff",
+ selectedColor: "white",
+ color: "#409eff",
+ borderColor: "#409eff",
+ fontWeight: "bold",
+ fontFamily: '"Noto Sans JP", "Noto Sans KR", "Meiryo UI", "Malgun Gothic", "Segoe UI", "sans-serif"'
+ },
+ size: {
+ height: 1.5,
+ width: 15,
+ padding: 0.1,
+ fontSize: 0.75
+ },
+ items: {
+ labels: [{ name: "Off" }, { name: "VIP" }, { name: "Friends" }, { name: "Everyone" }]
+ }
};
- $app.data.notificationOnlineOfflineFilter = configRepository.getString('VRCX_notificationOnlineOfflineFilter');
- $app.methods.changeNotificationOnlineOfflineFilter = function () {
- configRepository.setString('VRCX_notificationOnlineOfflineFilter', this.notificationOnlineOfflineFilter);
+ $app.data.toggleSwitchOptionsFriends = {
+ layout: {
+ backgroundColor: "white",
+ selectedBackgroundColor: "#409eff",
+ selectedColor: "white",
+ color: "#409eff",
+ borderColor: "#409eff",
+ fontWeight: "bold",
+ fontFamily: '"Noto Sans JP", "Noto Sans KR", "Meiryo UI", "Malgun Gothic", "Segoe UI", "sans-serif"'
+ },
+ size: {
+ height: 1.5,
+ width: 11.25,
+ padding: 0.1,
+ fontSize: 0.75
+ },
+ items: {
+ labels: [{ name: "Off" }, { name: "VIP" }, { name: "Friends" }]
+ }
};
+ $app.data.toggleSwitchOptionsOn = {
+ layout: {
+ backgroundColor: "white",
+ selectedBackgroundColor: "#409eff",
+ selectedColor: "white",
+ color: "#409eff",
+ borderColor: "#409eff",
+ fontWeight: "bold",
+ fontFamily: '"Noto Sans JP", "Noto Sans KR", "Meiryo UI", "Malgun Gothic", "Segoe UI", "sans-serif"'
+ },
+ size: {
+ height: 1.5,
+ width: 7.5,
+ padding: 0.1,
+ fontSize: 0.75
+ },
+ items: {
+ labels: [{ name: "Off" }, { name: "On" }]
+ }
+ };
+
+ $app.methods.saveSharedFeedFilters = function () {
+ this.notyFeedFiltersDialog.visible = false;
+ this.wristFeedFiltersDialog.visible = false;
+ configRepository.setString('sharedFeedFilters', JSON.stringify(this.sharedFeedFilters));
+ AppApi.RefreshVR();
+ }
+
+ $app.methods.cancelSharedFeedFilters = function () {
+ this.notyFeedFiltersDialog.visible = false;
+ this.wristFeedFiltersDialog.visible = false;
+ this.sharedFeedFilters = JSON.parse(configRepository.getString('sharedFeedFilters'));
+ }
+
$app.data.notificationPosition = configRepository.getString('VRCX_notificationPosition');
$app.methods.changeNotificationPosition = function () {
configRepository.setString('VRCX_notificationPosition', this.notificationPosition);
+ AppApi.RefreshVR();
};
+ sharedRepository.setBool('is_game_running', false);
var isGameRunningStateChange = function () {
+ sharedRepository.setBool('is_game_running', this.isGameRunning);
$app.lastLocation = '';
if (this.isGameRunning) {
API.currentUser.$online_for = Date.now();
@@ -5872,6 +5973,7 @@ speechSynthesis.getVoices();
var voiceName = voices[index].name;
speechSynthesis.cancel();
this.speak(voiceName);
+ AppApi.RefreshVR();
};
$app.methods.speak = function (text) {
@@ -7579,6 +7681,28 @@ speechSynthesis.getVoices();
this.notificationPositionDialog.visible = true;
};
+ // App: Noty feed filters
+
+ $app.data.notyFeedFiltersDialog = {
+ visible: false
+ };
+
+ $app.methods.showNotyFeedFiltersDialog = function () {
+ this.$nextTick(() => adjustDialogZ(this.$refs.notyFeedFiltersDialog.$el));
+ this.notyFeedFiltersDialog.visible = true;
+ };
+
+ // App: Wrist feed filters
+
+ $app.data.wristFeedFiltersDialog = {
+ visible: false
+ };
+
+ $app.methods.showWristFeedFiltersDialog = function () {
+ this.$nextTick(() => adjustDialogZ(this.$refs.wristFeedFiltersDialog.$el));
+ this.wristFeedFiltersDialog.visible = true;
+ };
+
// App: Launch Dialog
$app.data.launchDialog = {
diff --git a/html/src/app.scss b/html/src/app.scss
index 06d92a44..8cf13ef4 100644
--- a/html/src/app.scss
+++ b/html/src/app.scss
@@ -567,3 +567,19 @@ i.x-user-status.busy {
display: inline-block;
min-width: 175px;
}
+
+.toggle-switch {
+ display: inline-block;
+}
+
+.toggle-list {
+ font-size: 12px;
+}
+
+.toggle-list .toggle-name {
+ display: inline-block;
+ min-width: 100px;
+ padding: 2px 5px 2px 0;
+ vertical-align: top;
+ text-align: right;
+}
diff --git a/html/src/index.pug b/html/src/index.pug
index 4d3215df..37f6d765 100644
--- a/html/src/index.pug
+++ b/html/src/index.pug
@@ -515,18 +515,18 @@ html
span.name VRCPlus Profile Icons
el-switch(v-model="displayVRCPlusIconsAsAvatar")
div.options-container
- span.header Friends Sort Option
+ span.header Side Pannel Sorting Options
div.options-container-item
span.name VIP
el-switch(v-model="orderFriendsGroup0" inactive-text="by name" active-text="by state")
div.options-container-item
- span.name ONLINE
+ span.name Online
el-switch(v-model="orderFriendsGroup1" inactive-text="by name" active-text="by state")
div.options-container-item
- span.name ACTIVE
+ span.name Active
el-switch(v-model="orderFriendsGroup2" inactive-text="by name" active-text="by state")
div.options-container-item
- span.name OFFLINE
+ span.name Offline
el-switch(v-model="orderFriendsGroup3" inactive-text="by name" active-text="by state")
div.options-container
span.header Discord Presence
@@ -560,9 +560,6 @@ html
div.options-container-item
span.name Hide VR Devices
el-switch(v-model="hideDevicesFromFeed" :disabled="!openVR")
- div.options-container-item
- span.name Hide Online/Offline
- el-switch(v-model="hideLoginsFromFeed" :disabled="!openVR")
div.options-container-item
span.name Hide Private Worlds
el-switch(v-model="hidePrivateFromFeed" :disabled="!openVR")
@@ -581,24 +578,12 @@ html
el-button(v-text="TTSvoices[notificationTTSVoice].name" size="mini" :disabled="!openVR || !notificationTTS")
el-dropdown-menu(#default="dropdown")
el-dropdown-item(v-if="voice" v-for="(voice, index) in TTSvoices" :key="index" v-text="voice.name" :command="index")
+ 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()" :disabled="!overlayNotifications || !openVR") Notification Filters
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
- div.options-container-item
- span Join/Leave Notifications
- br
- el-radio-group(v-model="notificationJoinLeaveFilter" size="mini" @change="changeNotificationJoinLeaveFilter" :disabled="!overlayNotifications && !notificationTTS || !openVR")
- el-radio(label="VIP" v-model="notificationJoinLeaveFilter") VIP
- el-radio(label="Friends" v-model="notificationJoinLeaveFilter") Friends
- el-radio(label="Everyone" v-model="notificationJoinLeaveFilter") Everyone
- el-radio(label="Off" v-model="notificationJoinLeaveFilter") Off
- div.options-container-item
- span Online/Offline Notifications
- br
- el-radio-group(v-model="notificationOnlineOfflineFilter" size="mini" @change="changeNotificationOnlineOfflineFilter" :disabled="!overlayNotifications && !notificationTTS || !openVR")
- el-radio(label="VIP" v-model="notificationOnlineOfflineFilter") VIP
- el-radio(label="Friends" v-model="notificationOnlineOfflineFilter") Friends
- el-radio(label="Off" v-model="notificationOnlineOfflineFilter") Off
div.options-container
span.header Application
div.options-container-item
@@ -1206,6 +1191,107 @@ html
div(style="display:flex")
el-button(type="primary" size="small" style="margin-left:auto" @click="notificationPositionDialog.visible = false") OK
+ //- dialog: Noty feed filters
+ el-dialog.x-dialog(ref="notyFeedFiltersDialog" :visible.sync="notyFeedFiltersDialog.visible" title="Notification Filters" width="400px")
+ div.toggle-list
+ div
+ span.toggle-name OnPlayerJoining
+ toggle-switch(:options="toggleSwitchOptionsFriends" group="switchNotyGroupOnPlayerJoining" v-model="sharedFeedFilters.noty.OnPlayerJoining" class="toggle-switch")
+ div
+ span.toggle-name OnPlayerJoined
+ toggle-switch(:options="toggleSwitchOptionsEveryone" group="switchNotyGroupOnPlayerJoined" v-model="sharedFeedFilters.noty.OnPlayerJoined" class="toggle-switch")
+ div
+ span.toggle-name OnPlayerLeft
+ toggle-switch(:options="toggleSwitchOptionsEveryone" group="switchNotyGroupOnPlayerLeft" v-model="sharedFeedFilters.noty.OnPlayerLeft" class="toggle-switch")
+ div
+ span.toggle-name Online
+ toggle-switch(:options="toggleSwitchOptionsFriends" group="switchNotyGroupOnline" v-model="sharedFeedFilters.noty.Online" class="toggle-switch")
+ div
+ span.toggle-name Offline
+ toggle-switch(:options="toggleSwitchOptionsFriends" group="switchNotyGroupOffline" v-model="sharedFeedFilters.noty.Offline" class="toggle-switch")
+ div
+ span.toggle-name GPS
+ toggle-switch(:options="toggleSwitchOptionsFriends" group="switchNotyGroupGPS" v-model="sharedFeedFilters.noty.GPS" class="toggle-switch")
+ div
+ span.toggle-name Status
+ toggle-switch(:options="toggleSwitchOptionsFriends" group="switchNotyGroupStatus" v-model="sharedFeedFilters.noty.Status" class="toggle-switch")
+ div
+ span.toggle-name Invite
+ toggle-switch(:options="toggleSwitchOptionsFriends" group="switchNotyGroupinvite" v-model="sharedFeedFilters.noty.invite" class="toggle-switch")
+ div
+ span.toggle-name Request Invite
+ toggle-switch(:options="toggleSwitchOptionsFriends" group="switchNotyGrouprequestInvite" v-model="sharedFeedFilters.noty.requestInvite" class="toggle-switch")
+ div
+ span.toggle-name Friend Request
+ toggle-switch(:options="toggleSwitchOptionsOn" group="switchNotyGrouprequestfriendRequest" v-model="sharedFeedFilters.noty.friendRequest" class="toggle-switch")
+ div
+ span.toggle-name New Friend
+ toggle-switch(:options="toggleSwitchOptionsOn" group="switchNotyGrouprequestFriend" v-model="sharedFeedFilters.noty.Friend" class="toggle-switch")
+ div
+ span.toggle-name Unfriend
+ toggle-switch(:options="toggleSwitchOptionsOn" group="switchNotyGrouprequestUnfriend" v-model="sharedFeedFilters.noty.Unfriend" class="toggle-switch")
+ div
+ span.toggle-name DisplayName
+ toggle-switch(:options="toggleSwitchOptionsFriends" group="switchNotyGrouprequestDisplayName" v-model="sharedFeedFilters.noty.DisplayName" class="toggle-switch")
+ div
+ span.toggle-name TrustLevel
+ toggle-switch(:options="toggleSwitchOptionsFriends" group="switchNotyGrouprequestTrustLevel" v-model="sharedFeedFilters.noty.TrustLevel" class="toggle-switch")
+ template(#footer)
+ el-button(type="small" @click="cancelSharedFeedFilters") Cancel
+ el-button(type="primary" size="small" style="margin-left:10px" @click="saveSharedFeedFilters") Save
+
+ //- dialog: wrist feed filters
+ el-dialog.x-dialog(ref="wristFeedFiltersDialog" :visible.sync="wristFeedFiltersDialog.visible" title="Wrist Feed Filters" width="400px")
+ div.toggle-list
+ div
+ span.toggle-name Self Location
+ toggle-switch(:options="toggleSwitchOptionsOn" group="switchWristGroupLocation" v-model="sharedFeedFilters.wrist.Location" class="toggle-switch")
+ div
+ span.toggle-name OnPlayerJoining
+ toggle-switch(:options="toggleSwitchOptionsFriends" group="switchWristGroupOnPlayerJoining" v-model="sharedFeedFilters.wrist.OnPlayerJoining" class="toggle-switch")
+ div
+ span.toggle-name OnPlayerJoined
+ toggle-switch(:options="toggleSwitchOptionsEveryone" group="switchWristGroupOnPlayerJoined" v-model="sharedFeedFilters.wrist.OnPlayerJoined" class="toggle-switch")
+ div
+ span.toggle-name OnPlayerLeft
+ toggle-switch(:options="toggleSwitchOptionsEveryone" group="switchWristGroupOnPlayerLeft" v-model="sharedFeedFilters.wrist.OnPlayerLeft" class="toggle-switch")
+ div
+ span.toggle-name Online
+ toggle-switch(:options="toggleSwitchOptionsFriends" group="switchWristGroupOnline" v-model="sharedFeedFilters.wrist.Online" class="toggle-switch")
+ div
+ span.toggle-name Offline
+ toggle-switch(:options="toggleSwitchOptionsFriends" group="switchWristGroupOffline" v-model="sharedFeedFilters.wrist.Offline" class="toggle-switch")
+ div
+ span.toggle-name GPS
+ toggle-switch(:options="toggleSwitchOptionsFriends" group="switchWristGroupGPS" v-model="sharedFeedFilters.wrist.GPS" class="toggle-switch")
+ div
+ span.toggle-name Status
+ toggle-switch(:options="toggleSwitchOptionsFriends" group="switchWristGroupStatus" v-model="sharedFeedFilters.wrist.Status" class="toggle-switch")
+ div
+ span.toggle-name Invite
+ toggle-switch(:options="toggleSwitchOptionsFriends" group="switchWristGroupinvite" v-model="sharedFeedFilters.wrist.invite" class="toggle-switch")
+ div
+ span.toggle-name Request Invite
+ toggle-switch(:options="toggleSwitchOptionsFriends" group="switchWristGrouprequestInvite" v-model="sharedFeedFilters.wrist.requestInvite" class="toggle-switch")
+ div
+ span.toggle-name Friend Request
+ toggle-switch(:options="toggleSwitchOptionsOn" group="switchWristGrouprequestfriendRequest" v-model="sharedFeedFilters.wrist.friendRequest" class="toggle-switch")
+ div
+ span.toggle-name New Friend
+ toggle-switch(:options="toggleSwitchOptionsOn" group="switchWristGrouprequestFriend" v-model="sharedFeedFilters.wrist.Friend" class="toggle-switch")
+ div
+ span.toggle-name Unfriend
+ toggle-switch(:options="toggleSwitchOptionsOn" group="switchWristGrouprequestUnfriend" v-model="sharedFeedFilters.wrist.Unfriend" class="toggle-switch")
+ div
+ span.toggle-name DisplayName
+ toggle-switch(:options="toggleSwitchOptionsFriends" group="switchWristGrouprequestDisplayName" v-model="sharedFeedFilters.wrist.DisplayName" class="toggle-switch")
+ div
+ span.toggle-name TrustLevel
+ toggle-switch(:options="toggleSwitchOptionsFriends" group="switchWristGrouprequestTrustLevel" v-model="sharedFeedFilters.wrist.TrustLevel" class="toggle-switch")
+ template(#footer)
+ el-button(type="small" @click="cancelSharedFeedFilters") Cancel
+ el-button(type="primary" size="small" @click="saveSharedFeedFilters") Save
+
//- dialog: open source software notice
el-dialog.x-dialog(:visible.sync="ossDialog" title="Open Source Software Notice" width="650px")
div(style="height:350px;overflow:hidden scroll;word-break:break-all")
@@ -1480,6 +1566,30 @@ html
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+ div(style="margin-top:15px")
+ p(style="font-weight:bold") vuejs-toggle-switch
+ pre(style="font-size:12px;white-space:pre-line").
+ MIT License
+
+ Copyright (c) 2018 Lars-Martin Hejll
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
diff --git a/html/src/vr.js b/html/src/vr.js
index f43e6918..16b8d962 100644
--- a/html/src/vr.js
+++ b/html/src/vr.js
@@ -573,9 +573,21 @@ speechSynthesis.getVoices();
currentTime: new Date().toJSON(),
currentUserStatus: null,
cpuUsage: 0,
- feeds: [],
+ isGameRunning: false,
+ lastLocation: '',
+ lastFeedEntry: [],
+ feedFilters: [],
+ wristFeed: [],
+ notyMap: [],
devices: [],
+ overlayNotificationsToggle: false,
+ notificationTTSToggle: false,
+ notificationTTSVoice: '0',
+ hideDevicesToggle: false,
isMinimalFeed: false,
+ notificationPosition: 'topCenter',
+ notificationTimeout: '3000',
+ notificationTheme: 'relax'
},
computed: {},
methods: {},
@@ -600,6 +612,8 @@ speechSynthesis.getVoices();
// FIXME: 어케 복구하냐 이건
throw err;
}).then((args) => {
+ this.initConfigVars();
+ this.initNotyMap();
this.updateLoop();
this.updateCpuUsageLoop();
this.$nextTick(function () {
@@ -612,23 +626,70 @@ speechSynthesis.getVoices();
}
};
+ $app.methods.initConfigVars = function () {
+ this.notificationTTSToggle = configRepository.getBool('VRCX_notificationTTS');
+ this.notificationTTSVoice = configRepository.getString('VRCX_notificationTTSVoice');
+ this.overlayNotificationsToggle = configRepository.getBool('VRCX_overlayNotifications');
+ this.hidePrivateFromFeed = configRepository.getBool('VRCX_hidePrivateFromFeed');
+ this.hideDevicesToggle = configRepository.getBool('VRCX_hideDevicesFromFeed');
+ this.isMinimalFeed = configRepository.getBool('VRCX_minimalFeed');
+ this.feedFilters = JSON.parse(configRepository.getString('sharedFeedFilters'));
+ this.notificationPosition = configRepository.getString('VRCX_notificationPosition');
+ this.notificationTimeout = configRepository.getString('VRCX_notificationTimeout');
+ if (configRepository.getBool('isDarkMode')) {
+ this.notificationTheme = 'sunset';
+ } else {
+ this.notificationTheme = 'relax';
+ }
+ };
+
+ $app.methods.initNotyMap = function () {
+ var feeds = sharedRepository.getArray('feeds');
+ if (feeds === null) {
+ return;
+ }
+ var filter = this.feedFilters.noty;
+ var filtered = [];
+ feeds.forEach((feed) => {
+ if (filter[feed.type]) {
+ if ((filter[feed.type] !== 'Off') &&
+ ((filter[feed.type] === 'Everyone') || (filter[feed.type] === 'On') ||
+ ((filter[feed.type] === 'Friends') && (feed.isFriend)) ||
+ ((filter[feed.type] === 'VIP') && (feed.isFavorite)))) {
+ var displayName = '';
+ if (feed.data) {
+ displayName = feed.data;
+ } else if (feed.displayName) {
+ displayName = feed.displayName;
+ } else if (feed.senderUsername) {
+ displayName = feed.senderUsername;
+ }
+ if ((displayName) && (!this.notyMap[displayName]) ||
+ (this.notyMap[displayName] < feed.created_at)) {
+ this.notyMap[displayName] = feed.created_at;
+ }
+ }
+ }
+ });
+ };
+
$app.methods.updateLoop = async function () {
try {
this.currentTime = new Date().toJSON();
this.currentUserStatus = sharedRepository.getString('current_user_status');
this.isGameRunning = sharedRepository.getBool('is_game_running');
- if (configRepository.getBool('VRCX_hideDevicesFromFeed') === false) {
+ this.lastLocation = sharedRepository.getString('last_location');
+ if (!this.hideDevicesToggle) {
AppApi.GetVRDevices().then((devices) => {
devices.forEach((device) => {
device[2] = parseInt(device[2], 10);
});
this.devices = devices;
});
- }
- else {
+ } else {
this.devices = '';
}
- await this.updateSharedFeed();
+ await this.updateSharedFeeds();
} catch (err) {
console.error(err);
}
@@ -645,185 +706,228 @@ speechSynthesis.getVoices();
setTimeout(() => this.updateCpuUsageLoop(), 1000);
};
- $app.methods.updateSharedFeed = async function () {
- // TODO: block mute hideAvatar unfriend
- this.isMinimalFeed = configRepository.getBool('VRCX_minimalFeed');
- var notificationPosition = configRepository.getString('VRCX_notificationPosition');
- var notificationTimeout = configRepository.getString('VRCX_notificationTimeout');
- var notificationJoinLeaveFilter = configRepository.getString('VRCX_notificationJoinLeaveFilter');
- var notificationOnlineOfflineFilter = configRepository.getString('VRCX_notificationOnlineOfflineFilter');
- var theme = 'relax';
- if (configRepository.getBool('isDarkMode') === true) {
- theme = 'sunset';
- }
-
+ $app.methods.updateSharedFeeds = async function () {
var feeds = sharedRepository.getArray('feeds');
if (feeds === null) {
return;
}
-
- var _feeds = this.feeds;
- this.feeds = feeds;
-
- if ((this.appType === '2') && this.isGameRunning) {
- var map = {};
- _feeds.forEach((feed) => {
- if (feed.type === 'OnPlayerJoined' ||
- feed.type === 'OnPlayerLeft') {
- if (!map[feed.data] ||
- map[feed.data] < feed.created_at) {
- map[feed.data] = feed.created_at;
- }
- } else if (feed.type === 'Online' ||
- feed.type === 'Offline') {
- if (!map[feed.displayName] ||
- map[feed.displayName] < feed.created_at) {
- map[feed.displayName] = feed.created_at;
- }
- } else if (feed.type === 'invite' ||
- feed.type === 'requestInvite' ||
- feed.type === 'friendRequest') {
- if (!map[feed.senderUsername] ||
- map[feed.senderUsername] < feed.created_at) {
- map[feed.senderUsername] = feed.created_at;
- }
- } else if (feed.type === 'Friend' ||
- feed.type === 'Unfriend') {
- if (!map[feed.displayName] ||
- map[feed.displayName] < feed.created_at) {
- map[feed.displayName] = feed.created_at;
- }
- }
- });
- // disable notification on busy
- if (this.currentUserStatus === 'busy') {
- return;
- }
- var notys = [];
- this.feeds.forEach((feed) => {
- if (((notificationOnlineOfflineFilter === "Friends") && (feed.isFriend)) ||
- ((notificationOnlineOfflineFilter === "VIP") && (feed.isFavorite))) {
- if (feed.type === 'Online' ||
- feed.type === 'Offline') {
- if (!map[feed.displayName] ||
- map[feed.displayName] < feed.created_at) {
- map[feed.displayName] = feed.created_at;
- notys.push(feed);
- }
- }
- }
- if ((notificationJoinLeaveFilter === "Everyone") ||
- ((notificationJoinLeaveFilter === "Friends") && (feed.isFriend)) ||
- ((notificationJoinLeaveFilter === "VIP") && (feed.isFavorite))) {
- if (feed.type === 'OnPlayerJoined' ||
- feed.type === 'OnPlayerLeft') {
- if (!map[feed.data] ||
- map[feed.data] < feed.created_at) {
- map[feed.data] = feed.created_at;
- notys.push(feed);
- }
- }
- }
- if (feed.type === 'invite' ||
- feed.type === 'requestInvite' ||
- feed.type === 'friendRequest') {
- if (!map[feed.senderUsername] ||
- map[feed.senderUsername] < feed.created_at) {
- map[feed.senderUsername] = feed.created_at;
- notys.push(feed);
- }
- }
- if (feed.type === 'Friend' ||
- feed.type === 'Unfriend') {
- if (!map[feed.displayName] ||
- map[feed.displayName] < feed.created_at) {
- map[feed.displayName] = feed.created_at;
- notys.push(feed);
- }
- }
- });
- var bias = new Date(Date.now() - 60000).toJSON();
- var theme = 'relax';
- if (configRepository.getBool('isDarkMode') === true) {
- theme = 'sunset';
- }
- notys.forEach((noty) => {
- if (noty.created_at > bias) {
- if (configRepository.getBool('VRCX_overlayNotifications')) {
- var text = '';
- switch (noty.type) {
- case 'OnPlayerJoined':
- text = `${noty.data} has joined`;
- break;
- case 'OnPlayerLeft':
- text = `${noty.data} has left`;
- break;
- case 'Online':
- text = `${noty.displayName} has logged in`;
- break;
- case 'Offline':
- text = `${noty.displayName} has logged out`;
- break;
- case 'invite':
- text = `${noty.senderUsername} has invited you to ${noty.details.worldName}`;
- break;
- case 'requestInvite':
- text = `${noty.senderUsername} has requested an invite`;
- break;
- case 'friendRequest':
- text = `${noty.senderUsername} has sent you a friend request`;
- break;
- case 'Friend':
- text = `${noty.displayName} is now your friend`;
- break;
- case 'Unfriend':
- text = `${noty.displayName} has unfriended you`;
- break;
- }
- if (text) {
- new Noty({
- type: 'alert',
- theme: theme,
- timeout: notificationTimeout,
- layout: notificationPosition,
- text: text
- }).show();
- }
- }
- if (configRepository.getBool('VRCX_notificationTTS')) {
- switch (noty.type) {
- case 'OnPlayerJoined':
- this.speak(`${noty.data} has joined`);
- break;
- case 'OnPlayerLeft':
- this.speak(`${noty.data} has left`);
- break;
- case 'Online':
- this.speak(`${noty.displayName} has logged in`);
- break;
- case 'Offline':
- this.speak(`${noty.displayName} has logged out`);
- break;
- case 'invite':
- this.speak(`${noty.senderUsername} has invited you to ${noty.details.worldName}`);
- break;
- case 'requestInvite':
- this.speak(`${noty.senderUsername} has requested an invite`);
- break;
- case 'friendRequest':
- this.speak(`${noty.senderUsername} has sent you a friend request`);
- break;
- case 'Friend':
- this.speak(`${noty.displayName} is now your friend`);
- break;
- case 'Unfriend':
- this.speak(`${noty.displayName} has unfriended you`);
- break;
- }
- }
- }
- });
+ if ((this.lastFeedEntry !== undefined) &&
+ (feeds[0].created_at === this.lastFeedEntry.created_at)) {
+ return;
}
+ this.lastFeedEntry = feeds[0];
+
+ // OnPlayerJoining
+ var bias = new Date(Date.now() - 120000).toJSON();
+ for (i = 0; i < feeds.length; i++) {
+ var ctx = feeds[i];
+ if ((ctx.created_at < bias) || (ctx.type === 'Location')) {
+ break;
+ }
+ if ((ctx.type === 'GPS') && (ctx.location[0] === this.lastLocation)) {
+ var joining = true;
+ for (var k = 0; k < feeds.length; k++) {
+ var feedItem = feeds[k];
+ if ((feedItem.type === 'OnPlayerJoined') && (feedItem.data === ctx.displayName)) {
+ joining = false;
+ break;
+ }
+ if ((feedItem.created_at < bias) || (feedItem.type === 'Location') ||
+ ((feedItem.type === 'GPS') && (feedItem.location !== ctx.location[0]) &&
+ (feedItem.displayName === ctx.displayName))) {
+ break;
+ }
+ }
+ if (joining) {
+ var onPlayerJoining = {};
+ onPlayerJoining.created_at = ctx.created_at;
+ onPlayerJoining.data = ctx.displayName;
+ onPlayerJoining.isFavorite = ctx.isFavorite;
+ onPlayerJoining.isFriend = ctx.isFriend;
+ onPlayerJoining.type = 'OnPlayerJoining';
+ feeds.splice(i, 0, onPlayerJoining);
+ i++;
+ }
+ }
+ }
+
+ if (this.hidePrivateFromFeed) {
+ for (var i = 0; i < feeds.length; i++) {
+ var feed = feeds[i];
+ if ((feed.type === 'GPS') && (feed.location[0] === 'private')) {
+ feeds.splice(i, 1);
+ i--;
+ }
+ }
+ }
+
+ if (this.appType === '1') {
+ this.updateSharedFeedWrist(feeds);
+ }
+ if (this.appType === '2') {
+ this.updateSharedFeedNoty(feeds);
+ }
+ };
+
+ $app.methods.updateSharedFeedWrist = async function (feeds) {
+ var filter = this.feedFilters.wrist;
+ var filtered = [];
+ feeds.forEach((feed) => {
+ if (filter[feed.type]) {
+ if ((filter[feed.type] !== 'Off') &&
+ ((filter[feed.type] === 'Everyone') || (filter[feed.type] === 'On') ||
+ ((filter[feed.type] === 'Friends') && (feed.isFriend)) ||
+ ((filter[feed.type] === 'VIP') && (feed.isFavorite)))) {
+ filtered.push(feed);
+ }
+ } else {
+ console.error(`missing feed filter for ${feed.type}`);
+ filtered.push(feed);
+ }
+ });
+ this.wristFeed = filtered;
+ };
+
+ $app.methods.updateSharedFeedNoty = async function (feeds) {
+ var filter = this.feedFilters.noty;
+ var filtered = [];
+ feeds.forEach((feed) => {
+ if (filter[feed.type]) {
+ if ((filter[feed.type] !== 'Off') &&
+ ((filter[feed.type] === 'Everyone') || (filter[feed.type] === 'On') ||
+ ((filter[feed.type] === 'Friends') && (feed.isFriend)) ||
+ ((filter[feed.type] === 'VIP') && (feed.isFavorite)))) {
+ filtered.push(feed);
+ }
+ }
+ });
+ var notyToPlay = [];
+ filtered.forEach((feed) => {
+ var displayName = '';
+ if (feed.displayName) {
+ displayName = feed.displayName;
+ } else if (feed.senderUsername) {
+ displayName = feed.senderUsername;
+ } else if (feed.data) {
+ displayName = feed.data;
+ } else {
+ console.error('missing displayName');
+ }
+ if ((displayName) && (!this.notyMap[displayName]) ||
+ (this.notyMap[displayName] < feed.created_at)) {
+ this.notyMap[displayName] = feed.created_at;
+ notyToPlay.push(feed);
+ }
+ });
+
+ // disable notifications when busy or game isn't running
+ if ((this.currentUserStatus === 'busy') || (!this.isGameRunning)) {
+ return;
+ }
+ notyToPlay.forEach(async (noty) => {
+ if (this.overlayNotificationsToggle) {
+ var text = '';
+ switch (noty.type) {
+ case 'OnPlayerJoined':
+ text = `${noty.data} has joined`;
+ break;
+ case 'OnPlayerLeft':
+ text = `${noty.data} has left`;
+ break;
+ case 'OnPlayerJoining':
+ text = `${noty.data} is joining`;
+ break;
+ case 'GPS':
+ text = '' + noty.displayName + ' is in ' + await this.displayLocation(noty.location[0]);
+ break;
+ case 'Online':
+ text = `${noty.displayName} has logged in`;
+ break;
+ case 'Offline':
+ text = `${noty.displayName} has logged out`;
+ break;
+ case 'Status':
+ text = `${noty.displayName} status is now ${noty.status[0].status} ${noty.status[0].statusDescription}`;
+ break;
+ case 'invite':
+ text = `${noty.senderUsername} has invited you to ${noty.details.worldName}`;
+ break;
+ case 'requestInvite':
+ text = `${noty.senderUsername} has requested an invite`;
+ break;
+ case 'friendRequest':
+ text = `${noty.senderUsername} has sent you a friend request`;
+ break;
+ case 'Friend':
+ text = `${noty.displayName} is now your friend`;
+ break;
+ case 'Unfriend':
+ text = `${noty.displayName} has unfriended you`;
+ break;
+ case 'TrustLevel':
+ text = `${noty.displayName} trust level is now ${noty.trustLevel}`;
+ break;
+ case 'DisplayName':
+ text = `${noty.previousDisplayName} changed their name to ${noty.displayName}`;
+ break;
+ }
+ if (text) {
+ new Noty({
+ type: 'alert',
+ theme: this.notificationTheme,
+ timeout: this.notificationTimeout,
+ layout: this.notificationPosition,
+ text: text
+ }).show();
+ }
+ }
+ if (this.notificationTTSToggle) {
+ switch (noty.type) {
+ case 'OnPlayerJoined':
+ this.speak(`${noty.data} has joined`);
+ break;
+ case 'OnPlayerLeft':
+ this.speak(`${noty.data} has left`);
+ break;
+ case 'OnPlayerJoining':
+ this.speak(`${noty.data} is joining`);
+ break;
+ case 'GPS':
+ this.speak(noty.displayName + ' is in ' + await this.displayLocation(noty.location[0]));
+ break;
+ case 'Online':
+ this.speak(`${noty.displayName} has logged in`);
+ break;
+ case 'Offline':
+ this.speak(`${noty.displayName} has logged out`);
+ break;
+ case 'Status':
+ this.speak(`${noty.displayName} status is now ${noty.status[0].status} ${noty.status[0].statusDescription}`);
+ break;
+ case 'invite':
+ this.speak(`${noty.senderUsername} has invited you to ${noty.details.worldName}`);
+ break;
+ case 'requestInvite':
+ this.speak(`${noty.senderUsername} has requested an invite`);
+ break;
+ case 'friendRequest':
+ this.speak(`${noty.senderUsername} has sent you a friend request`);
+ break;
+ case 'Friend':
+ this.speak(`${noty.displayName} is now your friend`);
+ break;
+ case 'Unfriend':
+ this.speak(`${noty.displayName} has unfriended you`);
+ break;
+ case 'TrustLevel':
+ this.speak(`${noty.displayName} trust level is now ${noty.trustLevel}`);
+ break;
+ case 'DisplayName':
+ this.speak(`${noty.previousDisplayName} changed their name to ${noty.displayName}`);
+ break;
+ }
+ }
+ });
};
$app.methods.userStatusClass = function (user) {
@@ -842,10 +946,40 @@ speechSynthesis.getVoices();
return style;
};
+ $app.methods.displayLocation = async function (location) {
+ var text = '';
+ var L = API.parseLocation(location);
+ if (L.isOffline) {
+ text = 'Offline';
+ } else if (L.isPrivate) {
+ text = 'Private';
+ } else if (L.worldId) {
+ var ref = API.cachedWorlds.get(L.worldId);
+ if (ref === undefined) {
+ await API.getWorld({
+ worldId: L.worldId
+ }).then((args) => {
+ if (L.tag === location) {
+ if (L.instanceId) {
+ text = `${args.json.name} ${L.accessType}`;
+ } else {
+ text = args.json.name;
+ }
+ }
+ });
+ } else if (L.instanceId) {
+ text = `${ref.name} ${L.accessType}`;
+ } else {
+ text = ref.name;
+ }
+ }
+ return text;
+ };
+
$app.methods.speak = function (text) {
var tts = new SpeechSynthesisUtterance();
var voices = speechSynthesis.getVoices();
- var voiceIndex = configRepository.getString('VRCX_notificationTTSVoice');
+ var voiceIndex = this.notificationTTSVoice;
tts.voice = voices[voiceIndex];
tts.text = text;
speechSynthesis.speak(tts);
diff --git a/html/src/vr.pug b/html/src/vr.pug
index d7b64431..6ce58908 100644
--- a/html/src/vr.pug
+++ b/html/src/vr.pug
@@ -12,11 +12,11 @@ html
link(rel="stylesheet" href="https://fonts.googleapis.com/css?family=Noto+Sans+JP|Noto+Sans+KR&display=swap")
link(rel="stylesheet" href="vr.css")
body
- .x-app#x-app(style="display:none" :class="{ 'x-app-type': appType === '1' }")
+ .x-app#x-app(v-if="appType === '1'" class="x-app-type")
.x-container(style="flex:1")
.x-friend-list(ref="list" style="color:#aaa")
- template(v-if="isMinimalFeed === true")
- template(v-for="feed in feeds")
+ template(v-if="isMinimalFeed")
+ template(v-for="feed in wristFeed")
.x-friend-item(v-if="feed.type === 'GPS'" :class="{ friend: feed.isFriend, favorite: feed.isFavorite }")
.detail
span.extra
@@ -47,6 +47,12 @@ html
span.extra
span.time {{ feed.created_at | formatDate('HH:MI') }}
| ◀️ #[span.name(v-text="feed.data")]
+ div(v-else-if="feed.type === 'OnPlayerJoining'" class="x-friend-item" :class="{ friend: feed.isFriend, favorite: feed.isFavorite }")
+ .detail
+ span.extra
+ span.time {{ feed.created_at | formatDate('HH:MI') }}
+ span.spin ▶️
+ span.name(v-text="feed.data" style="margin-left:20px")
div(v-else-if="feed.type === 'Location'" class="x-friend-item" :class="{ friend: feed.isFriend, favorite: feed.isFavorite }")
.detail
span.extra
@@ -88,7 +94,7 @@ html
span.time {{ feed.created_at | formatDate('HH:MI') }}
| 🤝 #[span.name(v-text="feed.displayName")] {{ feed.previousTrustLevel }} #[i.el-icon-right] {{ feed.trustLevel }}
template(v-else)
- template(v-for="feed in feeds")
+ template(v-for="feed in wristFeed")
.x-friend-item(v-if="feed.type === 'GPS'" :class="{ friend: feed.isFriend, favorite: feed.isFavorite }")
.detail
span.extra
@@ -119,6 +125,11 @@ html
span.extra
span.time {{ feed.created_at | formatDate('HH:MI') }}
| #[span.name(v-text="feed.data")] has left
+ div(v-else-if="feed.type === 'OnPlayerJoining'" class="x-friend-item" :class="{ friend: feed.isFriend, favorite: feed.isFavorite }")
+ .detail
+ span.extra
+ span.time {{ feed.created_at | formatDate('HH:MI') }}
+ | #[span.name(v-text="feed.data")] is joining
div(v-else-if="feed.type === 'Location'" class="x-friend-item" :class="{ friend: feed.isFriend, favorite: feed.isFavorite }")
.detail
span.extra
diff --git a/html/src/vr.scss b/html/src/vr.scss
index fc85bf76..711d9fc1 100644
--- a/html/src/vr.scss
+++ b/html/src/vr.scss
@@ -274,3 +274,19 @@ i.x-user-status.joinme {
i.x-user-status.busy {
background: #f56c6c;
}
+
+.spin {
+ animation: rotation 2.5s infinite linear;
+ position: absolute;
+ width: 14px;
+ height: 28px;
+}
+
+@keyframes rotation {
+ from {
+ transform: rotate(0deg);
+ }
+ to {
+ transform: rotate(359deg);
+ }
+}