mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-05-07 14:56:06 +02:00
Add more log parsing for portals, video errors, shader keyword limit
This commit is contained in:
+107
-3
@@ -1,4 +1,4 @@
|
|||||||
// Copyright(c) 2019 pypy. All rights reserved.
|
// Copyright(c) 2019 pypy. All rights reserved.
|
||||||
//
|
//
|
||||||
// This work is licensed under the terms of the MIT license.
|
// This work is licensed under the terms of the MIT license.
|
||||||
// For a copy, see <https://opensource.org/licenses/MIT>.
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
@@ -28,6 +28,7 @@ namespace VRCX
|
|||||||
private readonly List<string[]> m_LogList;
|
private readonly List<string[]> m_LogList;
|
||||||
private Thread m_Thread;
|
private Thread m_Thread;
|
||||||
private bool m_ResetLog;
|
private bool m_ResetLog;
|
||||||
|
private static bool ShaderKeywordsLimitReached = false;
|
||||||
|
|
||||||
// NOTE
|
// NOTE
|
||||||
// FileSystemWatcher() is unreliable
|
// FileSystemWatcher() is unreliable
|
||||||
@@ -189,7 +190,10 @@ namespace VRCX
|
|||||||
{
|
{
|
||||||
offset += 2;
|
offset += 2;
|
||||||
if (ParseLogOnPlayerJoinedOrLeft(fileInfo, logContext, line, offset) == true ||
|
if (ParseLogOnPlayerJoinedOrLeft(fileInfo, logContext, line, offset) == true ||
|
||||||
ParseLogLocation(fileInfo, logContext, line, offset) == true)
|
ParseLogLocation(fileInfo, logContext, line, offset) == true ||
|
||||||
|
ParseLogPortalSpawn(fileInfo, logContext, line, offset) == true ||
|
||||||
|
ParseLogJoinBlocked(fileInfo, logContext, line, offset) == true ||
|
||||||
|
ParseLogVideoError(fileInfo, logContext, line, offset) == true ||
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -197,7 +201,8 @@ namespace VRCX
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ParseLogNotification(fileInfo, logContext, line, 34) == true)
|
if (ParseLogNotification(fileInfo, logContext, line, 34) == true ||
|
||||||
|
ParseLogShaderKeywordsLimit(fileInfo, logContext, line, 34) == true)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -270,6 +275,7 @@ namespace VRCX
|
|||||||
location,
|
location,
|
||||||
logContext.RecentWorldName
|
logContext.RecentWorldName
|
||||||
});
|
});
|
||||||
|
ShaderKeywordsLimitReached = false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -345,6 +351,104 @@ namespace VRCX
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool ParseLogPortalSpawn(FileInfo fileInfo, LogContext logContext, string line, int offset)
|
||||||
|
{
|
||||||
|
// 2021.04.06 11:25:45 Log - [Network Processing] RPC invoked ConfigurePortal on (Clone [1600004] Portals/PortalInternalDynamic) for Natsumi-sama
|
||||||
|
|
||||||
|
if (string.Compare(line, offset, "RPC invoked ConfigurePortal on (Clone [", 0, 39, StringComparison.Ordinal) != 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var pos = line.LastIndexOf("] Portals/PortalInternalDynamic) for ");
|
||||||
|
if (pos < 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//var portalId = line.Substring(offset + 39, pos - (offset + 39));
|
||||||
|
var data = line.Substring(pos + 37);
|
||||||
|
|
||||||
|
AppendLog(new[]
|
||||||
|
{
|
||||||
|
fileInfo.Name,
|
||||||
|
ConvertLogTimeToISO8601(line),
|
||||||
|
"portal-spawn",
|
||||||
|
data
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ParseLogShaderKeywordsLimit(FileInfo fileInfo, LogContext logContext, string line, int offset)
|
||||||
|
{
|
||||||
|
// 2021.04.04 12:21:06 Error - Maximum number (256) of shader keywords exceeded, keyword _TOGGLESIMPLEBLUR_ON will be ignored.
|
||||||
|
|
||||||
|
if (string.Compare(line, offset, "Maximum number (256) of shader keywords exceeded", 0, 48, StringComparison.Ordinal) != 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ShaderKeywordsLimitReached == true)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AppendLog(new[]
|
||||||
|
{
|
||||||
|
fileInfo.Name,
|
||||||
|
ConvertLogTimeToISO8601(line),
|
||||||
|
"event",
|
||||||
|
"Shader Keyword Limit has been reached"
|
||||||
|
});
|
||||||
|
ShaderKeywordsLimitReached = true;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ParseLogJoinBlocked(FileInfo fileInfo, LogContext logContext, string line, int offset)
|
||||||
|
{
|
||||||
|
// 2021.04.07 09:34:37 Error - [Behaviour] Master is not sending any events! Moving to a new instance.
|
||||||
|
|
||||||
|
if (string.Compare(line, offset, "Master is not sending any events! Moving to a new instance.", 0, 59, StringComparison.Ordinal) != 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AppendLog(new[]
|
||||||
|
{
|
||||||
|
fileInfo.Name,
|
||||||
|
ConvertLogTimeToISO8601(line),
|
||||||
|
"event",
|
||||||
|
"Joining instance blocked by master"
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ParseLogVideoError(FileInfo fileInfo, LogContext logContext, string line, int offset)
|
||||||
|
{
|
||||||
|
// 2021.04.08 06:37:45 Error - [Video Playback] ERROR: Video unavailable
|
||||||
|
// 2021.04.08 06:40:07 Error - [Video Playback] ERROR: Private video
|
||||||
|
|
||||||
|
if (string.Compare(line, offset, "ERROR: ", 0, 7, StringComparison.Ordinal) != 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var data = line.Substring(offset + 7);
|
||||||
|
|
||||||
|
AppendLog(new[]
|
||||||
|
{
|
||||||
|
fileInfo.Name,
|
||||||
|
ConvertLogTimeToISO8601(line),
|
||||||
|
"event",
|
||||||
|
"VideoError: " + data
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private bool ParseLogNotification(FileInfo fileInfo, LogContext logContext, string line, int offset)
|
private bool ParseLogNotification(FileInfo fileInfo, LogContext logContext, string line, int offset)
|
||||||
{
|
{
|
||||||
// 2021.01.03 05:48:58 Log - Received Notification: < Notification from username:pypy, sender user id:usr_4f76a584-9d4b-46f6-8209-8305eb683661 to of type: friendRequest, id: not_3a8f66eb-613c-4351-bee3-9980e6b5652c, created at: 01/14/2021 15:38:40 UTC, details: {{}}, type:friendRequest, m seen:False, message: ""> received at 01/02/2021 16:48:58 UTC
|
// 2021.01.03 05:48:58 Log - Received Notification: < Notification from username:pypy, sender user id:usr_4f76a584-9d4b-46f6-8209-8305eb683661 to of type: friendRequest, id: not_3a8f66eb-613c-4351-bee3-9980e6b5652c, created at: 01/14/2021 15:38:40 UTC, details: {{}}, type:friendRequest, m seen:False, message: ""> received at 01/02/2021 16:48:58 UTC
|
||||||
|
|||||||
@@ -4365,6 +4365,12 @@ speechSynthesis.getVoices();
|
|||||||
case 'unmute':
|
case 'unmute':
|
||||||
this.speak(`${noty.sourceDisplayName} has unmuted you`);
|
this.speak(`${noty.sourceDisplayName} has unmuted you`);
|
||||||
break;
|
break;
|
||||||
|
case 'PortalSpawn':
|
||||||
|
this.speak(`${noty.data} has spawned a portal`);
|
||||||
|
break;
|
||||||
|
case 'Event':
|
||||||
|
this.speak(noty.data);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -4436,6 +4442,12 @@ speechSynthesis.getVoices();
|
|||||||
case 'unmute':
|
case 'unmute':
|
||||||
AppApi.XSNotification('VRCX', `${noty.sourceDisplayName} has unmuted you`, timeout, image);
|
AppApi.XSNotification('VRCX', `${noty.sourceDisplayName} has unmuted you`, timeout, image);
|
||||||
break;
|
break;
|
||||||
|
case 'PortalSpawn':
|
||||||
|
AppApi.XSNotification('VRCX', `${noty.data} has spawned a portal`, timeout, image);
|
||||||
|
break;
|
||||||
|
case 'Event':
|
||||||
|
AppApi.XSNotification('VRCX', noty.data, timeout, image);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -4506,6 +4518,12 @@ speechSynthesis.getVoices();
|
|||||||
case 'unmute':
|
case 'unmute':
|
||||||
AppApi.DesktopNotification(noty.sourceDisplayName, `has unmuted you`, image);
|
AppApi.DesktopNotification(noty.sourceDisplayName, `has unmuted you`, image);
|
||||||
break;
|
break;
|
||||||
|
case 'PortalSpawn':
|
||||||
|
AppApi.DesktopNotification(noty.data, `has spawned a portal`, image);
|
||||||
|
break;
|
||||||
|
case 'Event':
|
||||||
|
AppApi.DesktopNotification('Event', noty.data, '');
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -5757,6 +5775,23 @@ speechSynthesis.getVoices();
|
|||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'portal-spawn':
|
||||||
|
tableData = {
|
||||||
|
created_at: gameLog.dt,
|
||||||
|
type: 'PortalSpawn',
|
||||||
|
data: gameLog.userDisplayName
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
case 'event':
|
||||||
|
tableData = {
|
||||||
|
created_at: gameLog.dt,
|
||||||
|
type: 'Event',
|
||||||
|
data: gameLog.event
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -6920,6 +6955,8 @@ speechSynthesis.getVoices();
|
|||||||
sharedFeedFilters.noty.block = 'On';
|
sharedFeedFilters.noty.block = 'On';
|
||||||
sharedFeedFilters.noty.mute = 'On';
|
sharedFeedFilters.noty.mute = 'On';
|
||||||
sharedFeedFilters.noty.unmute = 'On';
|
sharedFeedFilters.noty.unmute = 'On';
|
||||||
|
sharedFeedFilters.noty.PortalSpawn = 'Everyone';
|
||||||
|
sharedFeedFilters.noty.Event = 'On';
|
||||||
sharedFeedFilters.wrist.Location = 'On';
|
sharedFeedFilters.wrist.Location = 'On';
|
||||||
sharedFeedFilters.wrist.OnPlayerJoined = 'Everyone';
|
sharedFeedFilters.wrist.OnPlayerJoined = 'Everyone';
|
||||||
sharedFeedFilters.wrist.OnPlayerLeft = 'Everyone';
|
sharedFeedFilters.wrist.OnPlayerLeft = 'Everyone';
|
||||||
@@ -6942,6 +6979,8 @@ speechSynthesis.getVoices();
|
|||||||
sharedFeedFilters.wrist.block = 'On';
|
sharedFeedFilters.wrist.block = 'On';
|
||||||
sharedFeedFilters.wrist.mute = 'On';
|
sharedFeedFilters.wrist.mute = 'On';
|
||||||
sharedFeedFilters.wrist.unmute = 'On';
|
sharedFeedFilters.wrist.unmute = 'On';
|
||||||
|
sharedFeedFilters.wrist.PortalSpawn = 'Everyone';
|
||||||
|
sharedFeedFilters.wrist.Event = 'On';
|
||||||
|
|
||||||
configRepository.setString('sharedFeedFilters', JSON.stringify(sharedFeedFilters));
|
configRepository.setString('sharedFeedFilters', JSON.stringify(sharedFeedFilters));
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-2
@@ -167,7 +167,7 @@ html
|
|||||||
template(#tool)
|
template(#tool)
|
||||||
div(style="margin:0 0 10px;display:flex;align-items:center")
|
div(style="margin:0 0 10px;display:flex;align-items:center")
|
||||||
el-select(v-model="gameLogTable.filters[0].value" multiple clearable collapse-tags style="flex:1" placeholder="Filter")
|
el-select(v-model="gameLogTable.filters[0].value" multiple clearable collapse-tags style="flex:1" placeholder="Filter")
|
||||||
el-option(v-once v-for="type in ['Location', 'OnPlayerJoined', 'OnPlayerLeft', 'Notification']" :key="type" :label="type" :value="type")
|
el-option(v-once v-for="type in ['Location', 'OnPlayerJoined', 'OnPlayerLeft', 'Notification', 'PortalSpawn', 'Event']" :key="type" :label="type" :value="type")
|
||||||
el-input(v-model="gameLogTable.filters[1].value" placeholder="Search" style="flex:none;width:150px;margin:0 10px")
|
el-input(v-model="gameLogTable.filters[1].value" placeholder="Search" style="flex:none;width:150px;margin:0 10px")
|
||||||
el-button(type="default" @click="resetGameLog()" icon="el-icon-refresh" circle style="flex:none")
|
el-button(type="default" @click="resetGameLog()" icon="el-icon-refresh" circle style="flex:none")
|
||||||
el-table-column(type="expand")
|
el-table-column(type="expand")
|
||||||
@@ -184,7 +184,10 @@ html
|
|||||||
el-table-column(label="Detail" prop="data")
|
el-table-column(label="Detail" prop="data")
|
||||||
template(v-once #default="scope")
|
template(v-once #default="scope")
|
||||||
location(v-if="scope.row.type === 'Location'" :location="scope.row.data[0]" :hint="scope.row.data[1]")
|
location(v-if="scope.row.type === 'Location'" :location="scope.row.data[0]" :hint="scope.row.data[1]")
|
||||||
span.x-link(v-else-if="scope.row.type !== 'Notification'" v-text="scope.row.data" @click="lookupUser(scope.row.data)")
|
template(v-else-if="scope.row.type === 'Event'")
|
||||||
|
span(v-text="scope.row.data")
|
||||||
|
template(v-else-if="scope.row.type === 'Notification'")
|
||||||
|
span.x-link(v-else v-text="scope.row.data" @click="lookupUser(scope.row.data)")
|
||||||
|
|
||||||
//- search
|
//- search
|
||||||
.x-container(v-show="$refs.menu && $refs.menu.activeIndex === 'search'")
|
.x-container(v-show="$refs.menu && $refs.menu.activeIndex === 'search'")
|
||||||
@@ -1510,6 +1513,12 @@ html
|
|||||||
// div
|
// div
|
||||||
// span.toggle-name Unmute
|
// span.toggle-name Unmute
|
||||||
// toggle-switch(:options="toggleSwitchOptionsOn" group="switchNotyGrouprequestunmute" v-model="sharedFeedFilters.noty.unmute" class="toggle-switch")
|
// toggle-switch(:options="toggleSwitchOptionsOn" group="switchNotyGrouprequestunmute" v-model="sharedFeedFilters.noty.unmute" class="toggle-switch")
|
||||||
|
div
|
||||||
|
span.toggle-name Portal Spawn
|
||||||
|
toggle-switch(:options="toggleSwitchOptionsEveryone" group="switchNotyGrouprequestPortalSpawn" v-model="sharedFeedFilters.noty.PortalSpawn" class="toggle-switch")
|
||||||
|
div
|
||||||
|
span.toggle-name Events
|
||||||
|
toggle-switch(:options="toggleSwitchOptionsOn" group="switchNotyGrouprequestEvent" v-model="sharedFeedFilters.noty.Event" class="toggle-switch")
|
||||||
template(#footer)
|
template(#footer)
|
||||||
el-button(type="small" @click="cancelSharedFeedFilters") Cancel
|
el-button(type="small" @click="cancelSharedFeedFilters") Cancel
|
||||||
el-button(type="primary" size="small" style="margin-left:10px" @click="saveSharedFeedFilters") Save
|
el-button(type="primary" size="small" style="margin-left:10px" @click="saveSharedFeedFilters") Save
|
||||||
@@ -1583,6 +1592,12 @@ html
|
|||||||
// div
|
// div
|
||||||
// span.toggle-name Unmute
|
// span.toggle-name Unmute
|
||||||
// toggle-switch(:options="toggleSwitchOptionsOn" group="switchWristGrouprequestunmute" v-model="sharedFeedFilters.wrist.unmute" class="toggle-switch")
|
// toggle-switch(:options="toggleSwitchOptionsOn" group="switchWristGrouprequestunmute" v-model="sharedFeedFilters.wrist.unmute" class="toggle-switch")
|
||||||
|
div
|
||||||
|
span.toggle-name Portal Spawn
|
||||||
|
toggle-switch(:options="toggleSwitchOptionsEveryone" group="switchWristGrouprequestPortalSpawn" v-model="sharedFeedFilters.wrist.PortalSpawn" class="toggle-switch")
|
||||||
|
div
|
||||||
|
span.toggle-name Events
|
||||||
|
toggle-switch(:options="toggleSwitchOptionsOn" group="switchWristGrouprequestEvent" v-model="sharedFeedFilters.wrist.Event" class="toggle-switch")
|
||||||
template(#footer)
|
template(#footer)
|
||||||
el-button(type="small" @click="cancelSharedFeedFilters") Cancel
|
el-button(type="small" @click="cancelSharedFeedFilters") Cancel
|
||||||
el-button(type="primary" size="small" @click="saveSharedFeedFilters") Save
|
el-button(type="primary" size="small" @click="saveSharedFeedFilters") Save
|
||||||
|
|||||||
@@ -28,6 +28,14 @@ function parseRawGameLog(dt, type, args) {
|
|||||||
gameLog.json = args[0];
|
gameLog.json = args[0];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'portal-spawn':
|
||||||
|
gameLog.userDisplayName = args[0];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'event':
|
||||||
|
gameLog.event = args[0];
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -952,6 +952,12 @@ speechSynthesis.getVoices();
|
|||||||
case 'unmute':
|
case 'unmute':
|
||||||
text = `<strong>${noty.sourceDisplayName}</strong> has unmuted you`;
|
text = `<strong>${noty.sourceDisplayName}</strong> has unmuted you`;
|
||||||
break;
|
break;
|
||||||
|
case 'PortalSpawn':
|
||||||
|
text = `<strong>${noty.data}</strong> has spawned a portal`;
|
||||||
|
break;
|
||||||
|
case 'Event':
|
||||||
|
text = noty.data;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
+23
-3
@@ -117,17 +117,27 @@ html
|
|||||||
.detail
|
.detail
|
||||||
span.extra
|
span.extra
|
||||||
span.time {{ feed.created_at | formatDate('HH:MI') }}
|
span.time {{ feed.created_at | formatDate('HH:MI') }}
|
||||||
| 🛑 #[span.name(v-text="feed.sourceDisplayName")] has blocked you
|
| 🛑 #[span.name(v-text="feed.sourceDisplayName")]
|
||||||
div(v-else-if="feed.type === 'mute'" class="x-friend-item" :class="{ friend: feed.isFriend, favorite: feed.isFavorite }")
|
div(v-else-if="feed.type === 'mute'" class="x-friend-item" :class="{ friend: feed.isFriend, favorite: feed.isFavorite }")
|
||||||
.detail
|
.detail
|
||||||
span.extra
|
span.extra
|
||||||
span.time {{ feed.created_at | formatDate('HH:MI') }}
|
span.time {{ feed.created_at | formatDate('HH:MI') }}
|
||||||
| 🔇 #[span.name(v-text="feed.sourceDisplayName")] has muted you
|
| 🔇 #[span.name(v-text="feed.sourceDisplayName")]
|
||||||
div(v-else-if="feed.type === 'unmute'" class="x-friend-item" :class="{ friend: feed.isFriend, favorite: feed.isFavorite }")
|
div(v-else-if="feed.type === 'unmute'" class="x-friend-item" :class="{ friend: feed.isFriend, favorite: feed.isFavorite }")
|
||||||
.detail
|
.detail
|
||||||
span.extra
|
span.extra
|
||||||
span.time {{ feed.created_at | formatDate('HH:MI') }}
|
span.time {{ feed.created_at | formatDate('HH:MI') }}
|
||||||
| 🎤 #[span.name(v-text="feed.sourceDisplayName")] has unmuted you
|
| 🎤 #[span.name(v-text="feed.sourceDisplayName")]
|
||||||
|
div(v-else-if="feed.type === 'PortalSpawn'" 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")]
|
||||||
|
div(v-else-if="feed.type === 'Event'" class="x-friend-item")
|
||||||
|
.detail
|
||||||
|
span.extra
|
||||||
|
span.time {{ feed.created_at | formatDate('HH:MI') }}
|
||||||
|
| 🛑 #[span.name(v-text="feed.data")]
|
||||||
template(v-else)
|
template(v-else)
|
||||||
template(v-for="feed in wristFeed")
|
template(v-for="feed in wristFeed")
|
||||||
.x-friend-item(v-if="feed.type === 'GPS'" :class="{ friend: feed.isFriend, favorite: feed.isFavorite }")
|
.x-friend-item(v-if="feed.type === 'GPS'" :class="{ friend: feed.isFriend, favorite: feed.isFavorite }")
|
||||||
@@ -240,6 +250,16 @@ html
|
|||||||
span.extra
|
span.extra
|
||||||
span.time {{ feed.created_at | formatDate('HH:MI') }}
|
span.time {{ feed.created_at | formatDate('HH:MI') }}
|
||||||
| #[span.name(v-text="feed.sourceDisplayName")] has unmuted you
|
| #[span.name(v-text="feed.sourceDisplayName")] has unmuted you
|
||||||
|
div(v-else-if="feed.type === 'PortalSpawn'" 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")] has spawned a portal
|
||||||
|
div(v-else-if="feed.type === 'Event'" class="x-friend-item")
|
||||||
|
.detail
|
||||||
|
span.extra
|
||||||
|
span.time {{ feed.created_at | formatDate('HH:MI') }}
|
||||||
|
| Event: #[span.name(v-text="feed.data")]
|
||||||
.x-containerbottom
|
.x-containerbottom
|
||||||
div(style="display:flex;flex-direction:row")
|
div(style="display:flex;flex-direction:row")
|
||||||
template(v-if="devices.length")
|
template(v-if="devices.length")
|
||||||
|
|||||||
Reference in New Issue
Block a user