summaryrefslogtreecommitdiff
path: root/src/SMAPI.Web/Views/LogParser
diff options
context:
space:
mode:
authorKhloe Leclair <xivkhloeleclair@gmail.com>2022-04-08 14:59:52 -0400
committerKhloe Leclair <xivkhloeleclair@gmail.com>2022-04-08 14:59:52 -0400
commit0beff189d19416dfcbb64bce800af41de37ccd08 (patch)
treeab10d19b4b492abbc32a40630810738507d75b50 /src/SMAPI.Web/Views/LogParser
parentdf955c0d3e0110be0893082de0c82187de63d9d2 (diff)
downloadSMAPI-0beff189d19416dfcbb64bce800af41de37ccd08.tar.gz
SMAPI-0beff189d19416dfcbb64bce800af41de37ccd08.tar.bz2
SMAPI-0beff189d19416dfcbb64bce800af41de37ccd08.zip
Implement client-side log rendering, better filtering, and pagination to improve performance and enhance the user experience with using the log parser.
Diffstat (limited to 'src/SMAPI.Web/Views/LogParser')
-rw-r--r--src/SMAPI.Web/Views/LogParser/Index.cshtml143
1 files changed, 88 insertions, 55 deletions
diff --git a/src/SMAPI.Web/Views/LogParser/Index.cshtml b/src/SMAPI.Web/Views/LogParser/Index.cshtml
index c26ec230..39a2da0f 100644
--- a/src/SMAPI.Web/Views/LogParser/Index.cshtml
+++ b/src/SMAPI.Web/Views/LogParser/Index.cshtml
@@ -20,6 +20,16 @@
.Cast<LogLevel>()
.ToDictionary(level => level.ToString().ToLower(), level => level != LogLevel.Trace);
+ IDictionary<int, string> logLevels = Enum
+ .GetValues(typeof(LogLevel))
+ .Cast<LogLevel>()
+ .ToDictionary(level => (int)level, level => level.ToString().ToLower());
+
+ IDictionary<int, string> logSections = Enum
+ .GetValues(typeof(LogSection))
+ .Cast<LogSection>()
+ .ToDictionary(section => (int)section, section => section.ToString());
+
string curPageUrl = this.Url.PlainAction("Index", "LogParser", new { id = Model.PasteID }, absoluteUrl: true);
ISet<int> screenIds = new HashSet<int>(log?.Messages?.Select(p => p.ScreenId) ?? Array.Empty<int>());
@@ -34,8 +44,15 @@
<link rel="stylesheet" href="~/Content/css/log-parser.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/tabbyjs@12.0.3/dist/css/tabby-ui-vertical.min.css" />
+ @if (!Model.ShowRaw)
+ {
+ <script id="logSections" type="application/json">@this.ForJson(logSections)</script>
+ <script id="logLevels" type="application/json">@this.ForJson(logLevels)</script>
+ <script id="parsedMessages" type="application/json">@this.ForJson(log?.Messages)</script>
+ <script id="modSlugs" type="application/json">@this.ForJson(log?.Mods?.DistinctBy(m => m.Name).ToDictionary(m => m.Name, m => Model.GetSlug(m.Name)))</script>
+ }
<script src="https://cdn.jsdelivr.net/npm/tabbyjs@12.0.3" crossorigin="anonymous"></script>
- <script src="https://cdn.jsdelivr.net/npm/vue@2.6.11" crossorigin="anonymous"></script>
+ <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1" crossorigin="anonymous"></script>
<script src="~/Content/js/file-upload.js"></script>
<script src="~/Content/js/log-parser.js"></script>
@@ -275,29 +292,35 @@ else if (log?.IsValid == true)
<span class="notice txt"><i>click any mod to filter</i></span>
<span class="notice btn txt" v-on:click="showAllMods" v-bind:class="{ invisible: !anyModsHidden }">show all</span>
<span class="notice btn txt" v-on:click="hideAllMods" v-bind:class="{ invisible: !anyModsShown || !anyModsHidden }">hide all</span>
+ <span class="notice btn txt" v-on:click="toggleContentPacks">toggle content packs</span>
}
</caption>
@foreach (var mod in log.Mods.Where(p => p.Loaded && !p.IsContentPack))
{
+ LogModInfo[]? contentPackList;
+ if (contentPacks == null || !contentPacks.TryGetValue(mod.Name, out contentPackList))
+ contentPackList = null;
+
<tr v-on:click="toggleMod('@Model.GetSlug(mod.Name)')" class="mod-entry" v-bind:class="{ hidden: !showMods['@Model.GetSlug(mod.Name)'] }">
<td><input type="checkbox" v-bind:checked="showMods['@Model.GetSlug(mod.Name)']" v-bind:class="{ invisible: !anyModsHidden }" /></td>
- <td v-pre>
- <strong>@mod.Name</strong> @mod.Version
- @if (contentPacks != null && contentPacks.TryGetValue(mod.Name, out LogModInfo[] contentPackList))
+ <td>
+ <strong v-pre>@mod.Name</strong> @mod.Version
+ @if (contentPackList != null)
{
- <div class="content-packs">
+ <div v-if="!hideContentPacks" class="content-packs">
@foreach (var contentPack in contentPackList)
{
<text>+ @contentPack.Name @contentPack.Version</text><br />
}
</div>
+ <span v-else class="content-packs--short"> (+ @contentPackList.Length Content Packs)</span>
}
</td>
- <td v-pre>
+ <td>
@mod.Author
- @if (contentPacks != null && contentPacks.TryGetValue(mod.Name, out contentPackList))
+ @if (contentPackList != null)
{
- <div class="content-packs">
+ <div v-if="!hideContentPacks" class="content-packs">
@foreach (var contentPack in contentPackList)
{
<text>+ @contentPack.Author</text><br />
@@ -323,57 +346,67 @@ else if (log?.IsValid == true)
@if (!Model.ShowRaw)
{
+ <div id="filterHolder"></div>
<div id="filters">
- Filter messages:
- <span v-bind:class="{ active: showLevels['trace'] }" v-on:click="toggleLevel('trace')">TRACE</span> |
- <span v-bind:class="{ active: showLevels['debug'] }" v-on:click="toggleLevel('debug')">DEBUG</span> |
- <span v-bind:class="{ active: showLevels['info'] }" v-on:click="toggleLevel('info')">INFO</span> |
- <span v-bind:class="{ active: showLevels['alert'] }" v-on:click="toggleLevel('alert')">ALERT</span> |
- <span v-bind:class="{ active: showLevels['warn'] }" v-on:click="toggleLevel('warn')">WARN</span> |
- <span v-bind:class="{ active: showLevels['error'] }" v-on:click="toggleLevel('error')">ERROR</span>
+ <div class="toggles">
+ <div>
+ Filter messages:
+ </div>
+ <div>
+ <span role="button" v-bind:class="{ active: showLevels['trace'] }" v-on:click="toggleLevel('trace')">TRACE</span> |
+ <span role="button" v-bind:class="{ active: showLevels['debug'] }" v-on:click="toggleLevel('debug')">DEBUG</span> |
+ <span role="button" v-bind:class="{ active: showLevels['info'] }" v-on:click="toggleLevel('info')">INFO</span> |
+ <span role="button" v-bind:class="{ active: showLevels['alert'] }" v-on:click="toggleLevel('alert')">ALERT</span> |
+ <span role="button" v-bind:class="{ active: showLevels['warn'] }" v-on:click="toggleLevel('warn')">WARN</span> |
+ <span role="button" v-bind:class="{ active: showLevels['error'] }" v-on:click="toggleLevel('error')">ERROR</span>
+ <div class="filter-text">
+ <input
+ type="text"
+ v-bind:class="{ active: filterText && filterText != '' }"
+ v-on:input="updateFilterText"
+ placeholder="filter..."
+ />
+ <span role="button" v-bind:class="{ active: filterUseRegex }" v-on:click="toggleFilterUseRegex" title="Use Regular Expression">.*</span>
+ <span role="button" v-bind:class="{ active: !filterInsensitive }" v-on:click="toggleFilterInsensitive" title="Match Case">aA</span>
+ <span role="button" v-bind:class="{ active: filterUseWord, 'whole-word': true }" v-on:click="toggleFilterWord" title="Match Whole Word"><i>Ab</i></span>
+ <span role="button" v-bind:class="{ active: shouldHighlight }" v-on:click="toggleHighlight" title="Highlight Matches">HL</span>
+ </div>
+ <filter-stats
+ v-bind:start="start"
+ v-bind:end="end"
+ v-bind:pages="totalPages"
+ v-bind:filtered="filteredMessages.length"
+ v-bind:total="totalMessages"
+ />
+ </div>
+ </div>
+ <pager
+ v-bind:page="page"
+ v-bind:pages="totalPages"
+ v-bind:prevPage="prevPage"
+ v-bind:nextPage="nextPage"
+ />
</div>
- <table id="log">
- @foreach (var message in log.Messages)
- {
- string levelStr = message.Level.ToString().ToLower();
- string sectionStartClass = message.IsStartOfSection ? "section-start" : null;
- string sectionFilter = message.Section != null && !message.IsStartOfSection ? $"&& sectionsAllow('{message.Section}')" : null; // filter the message by section if applicable
+ <noscript>
+ <div>
+ This website uses JavaScript to display a filterable table. To view this log, please either
+ <a href="@this.Url.PlainAction("Index", "LogParser", new { id = Model.PasteID, format = LogViewFormat.RawView })">view the raw log</a>
+ or enable JavaScript.
+ </div>
+ <br/>
+ </noscript>
- <tr class="mod @levelStr @sectionStartClass"
- @if (message.IsStartOfSection) { <text> v-on:click="toggleSection('@message.Section')" </text> }
- v-show="filtersAllow('@Model.GetSlug(message.Mod)', '@levelStr') @sectionFilter">
- <td v-pre>@message.Time</td>
- @if (screenIds.Count > 1)
- {
- <td v-pre>screen_@message.ScreenId</td>
- }
- <td v-pre>@message.Level.ToString().ToUpper()</td>
- <td v-pre data-title="@message.Mod">@message.Mod</td>
- <td>
- <span v-pre class="log-message-text">@message.Text</span>
- @if (message.IsStartOfSection)
- {
- <span class="section-toggle-message">
- <template v-if="sectionsAllow('@message.Section')">
- This section is shown. Click here to hide it.
- </template>
- <template v-else>
- This section is hidden. Click here to show it.
- </template>
- </span>
- }
- </td>
- </tr>
- if (message.Repeated > 0)
- {
- <tr class="@levelStr mod mod-repeat" v-show="filtersAllow('@Model.GetSlug(message.Mod)', '@levelStr') @sectionFilter">
- <td colspan="4"></td>
- <td v-pre><i>repeats [@message.Repeated] times.</i></td>
- </tr>
- }
- }
- </table>
+ <log-table>
+ <log-line
+ v-for="msg in visibleMessages"
+ v-bind:key="msg.id"
+ v-bind:showScreenId="showScreenId"
+ v-bind:message="msg"
+ v-bind:highlight="shouldHighlight"
+ v-bind:sectionExpanded="msg.SectionName && visibleSections.includes(msg.SectionName)"
+ />
+ </log-table>
}
else
{