1
0
mirror of https://github.com/chylex/Discord-History-Tracker.git synced 2024-10-17 17:42:51 +02:00

Compare commits

...

7 Commits

7 changed files with 45 additions and 31 deletions

View File

@ -9,7 +9,7 @@ using DHT.Utils.Tasks;
namespace DHT.Desktop.Main.Controls { namespace DHT.Desktop.Main.Controls {
sealed class AttachmentFilterPanelModel : BaseModel, IDisposable { sealed class AttachmentFilterPanelModel : BaseModel, IDisposable {
public sealed record Unit(string Name, int Scale); public sealed record Unit(string Name, uint Scale);
private static readonly Unit[] AllUnits = { private static readonly Unit[] AllUnits = {
new ("B", 1), new ("B", 1),
@ -26,7 +26,7 @@ namespace DHT.Desktop.Main.Controls {
public string FilterStatisticsText { get; private set; } = ""; public string FilterStatisticsText { get; private set; } = "";
private bool limitSize = false; private bool limitSize = false;
private int maximumSize = 0; private ulong maximumSize = 0L;
private Unit maximumSizeUnit = AllUnits[0]; private Unit maximumSizeUnit = AllUnits[0];
public bool LimitSize { public bool LimitSize {
@ -34,7 +34,7 @@ namespace DHT.Desktop.Main.Controls {
set => Change(ref limitSize, value); set => Change(ref limitSize, value);
} }
public int MaximumSize { public ulong MaximumSize {
get => maximumSize; get => maximumSize;
set => Change(ref maximumSize, value); set => Change(ref maximumSize, value);
} }
@ -116,7 +116,11 @@ namespace DHT.Desktop.Main.Controls {
AttachmentFilter filter = new(); AttachmentFilter filter = new();
if (LimitSize) { if (LimitSize) {
try {
filter.MaxBytes = maximumSize * maximumSizeUnit.Scale; filter.MaxBytes = maximumSize * maximumSizeUnit.Scale;
} catch (ArithmeticException) {
// set no size limit, because the overflown size is larger than any file could possibly be
}
} }
return filter; return filter;

View File

@ -169,7 +169,7 @@ namespace DHT.Desktop.Main.Controls {
var exportedMessageCountStr = exportedMessageCount?.Format() ?? "(...)"; var exportedMessageCountStr = exportedMessageCount?.Format() ?? "(...)";
var totalMessageCountStr = totalMessageCount?.Format() ?? "(...)"; var totalMessageCountStr = totalMessageCount?.Format() ?? "(...)";
FilterStatisticsText = verb + " " + exportedMessageCountStr + " out of " + totalMessageCountStr + " message" + (totalMessageCount is null or 0 ? "." : "s."); FilterStatisticsText = verb + " " + exportedMessageCountStr + " out of " + totalMessageCountStr + " message" + (totalMessageCount is null or 1 ? "." : "s.");
OnPropertyChanged(nameof(FilterStatisticsText)); OnPropertyChanged(nameof(FilterStatisticsText));
} }

View File

@ -175,7 +175,6 @@ namespace DHT.Desktop.Main.Pages {
}; };
db.RemoveDownloadItems(allExceptFailedFilter, FilterRemovalMode.KeepMatching); db.RemoveDownloadItems(allExceptFailedFilter, FilterRemovalMode.KeepMatching);
downloadStatisticsComputer.Recompute();
if (IsDownloading) { if (IsDownloading) {
EnqueueDownloadItems(); EnqueueDownloadItems();

View File

@ -1,7 +1,7 @@
const DISCORD = (function() { const DISCORD = (function() {
const regex = { const regex = {
formatBold: /\*\*([\s\S]+?)\*\*(?!\*)/g, formatBold: /\*\*([\s\S]+?)\*\*(?!\*)/g,
formatItalic: /(.)?\*([\s\S]+?)\*(?!\*)/g, formatItalic: /(.)?([_*])([\s\S]+?)\2(?!\2)/g,
formatUnderline: /__([\s\S]+?)__(?!_)/g, formatUnderline: /__([\s\S]+?)__(?!_)/g,
formatStrike: /~~([\s\S]+?)~~(?!~)/g, formatStrike: /~~([\s\S]+?)~~(?!~)/g,
formatCodeInline: /(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/g, formatCodeInline: /(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/g,
@ -9,7 +9,7 @@ const DISCORD = (function() {
formatUrl: /(\b(?:https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig, formatUrl: /(\b(?:https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig,
formatUrlNoEmbed: /<(\b(?:https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])>/ig, formatUrlNoEmbed: /<(\b(?:https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])>/ig,
specialEscapedBacktick: /\\`/g, specialEscapedBacktick: /\\`/g,
specialEscapedSingle: /\\([*\\])/g, specialEscapedSingle: /\\([*_\\])/g,
specialEscapedDouble: /\\__|_\\_|\\_\\_|\\~~|~\\~|\\~\\~/g, specialEscapedDouble: /\\__|_\\_|\\_\\_|\\~~|~\\~|\\~\\~/g,
specialUnescaped: /([*_~\\])/g, specialUnescaped: /([*_~\\])/g,
mentionRole: /&lt;@&(\d+?)&gt;/g, mentionRole: /&lt;@&(\d+?)&gt;/g,
@ -47,8 +47,8 @@ const DISCORD = (function() {
.replace(regex.specialEscapedSingle, escapeHtmlMatch) .replace(regex.specialEscapedSingle, escapeHtmlMatch)
.replace(regex.specialEscapedDouble, full => full.replace(/\\/g, "").replace(/(.)/g, escapeHtmlMatch)) .replace(regex.specialEscapedDouble, full => full.replace(/\\/g, "").replace(/(.)/g, escapeHtmlMatch))
.replace(regex.formatBold, "<b>$1</b>") .replace(regex.formatBold, "<b>$1</b>")
.replace(regex.formatItalic, (full, pre, match) => pre === "\\" ? full : (pre || "") + "<i>" + match + "</i>")
.replace(regex.formatUnderline, "<u>$1</u>") .replace(regex.formatUnderline, "<u>$1</u>")
.replace(regex.formatItalic, (full, pre, char, match) => pre === "\\" ? full : (pre || "") + "<i>" + match + "</i>")
.replace(regex.formatStrike, "<s>$1</s>"); .replace(regex.formatStrike, "<s>$1</s>");
} }
@ -128,12 +128,12 @@ const DISCORD = (function() {
// noinspection HtmlUnknownTarget // noinspection HtmlUnknownTarget
templateEmbedImage = new TEMPLATE([ templateEmbedImage = new TEMPLATE([
"<a href='{url}' class='embed thumbnail'><img src='{src}' alt='(image attachment is loading...)' onerror='DISCORD.handleImageLoadError(this)'></a><br>" "<a href='{url}' class='embed thumbnail loading'><img src='{src}' alt='' onload='DISCORD.handleImageLoad(this)' onerror='DISCORD.handleImageLoadError(this)'></a><br>"
].join("")); ].join(""));
// noinspection HtmlUnknownTarget // noinspection HtmlUnknownTarget
templateEmbedImageWithSize = new TEMPLATE([ templateEmbedImageWithSize = new TEMPLATE([
"<a href='{url}' class='embed thumbnail'><img src='{src}' width='{width}' height='{height}' alt='(image attachment is loading...)' onerror='DISCORD.handleImageLoadError(this)'></a><br>" "<a href='{url}' class='embed thumbnail loading'><img src='{src}' width='{width}' height='{height}' alt='' onload='DISCORD.handleImageLoad(this)' onerror='DISCORD.handleImageLoadError(this)'></a><br>"
].join("")); ].join(""));
// noinspection HtmlUnknownTarget // noinspection HtmlUnknownTarget
@ -164,9 +164,14 @@ const DISCORD = (function() {
].join("")); ].join(""));
}, },
handleImageLoad(ele) {
ele.parentElement.classList.remove("loading");
},
handleImageLoadError(ele) { handleImageLoadError(ele) {
// noinspection JSUnusedGlobalSymbols // noinspection JSUnusedGlobalSymbols
ele.onerror = null; ele.onerror = null;
ele.parentElement.classList.remove("loading");
ele.setAttribute("alt", "(image attachment not found)"); ele.setAttribute("alt", "(image attachment not found)");
}, },

View File

@ -107,10 +107,23 @@
} }
.message .thumbnail { .message .thumbnail {
position: relative;
max-width: calc(100% - 20px); max-width: calc(100% - 20px);
max-height: 320px; max-height: 320px;
} }
.message .thumbnail.loading::after {
content: "";
background: rgba(0, 0, 0, 0.75)
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 300 300' preserveAspectRatio='xMidYMid'%3E %3Ccircle cx='150' cy='150' fill='none' stroke='%237983f5' stroke-width='8' r='42' stroke-dasharray='198 68'%3E %3CanimateTransform attributeName='transform' type='rotate' repeatCount='indefinite' dur='1.25s' values='0 150 150;360 150 150' keyTimes='0;1' /%3E %3C/circle%3E %3C/svg%3E")
no-repeat center center;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.message .thumbnail img { .message .thumbnail img {
width: auto; width: auto;
max-width: 100%; max-width: 100%;

View File

@ -1,6 +1,6 @@
namespace DHT.Server.Data.Filters { namespace DHT.Server.Data.Filters {
public sealed class AttachmentFilter { public sealed class AttachmentFilter {
public long? MaxBytes { get; set; } = null; public ulong? MaxBytes { get; set; } = null;
public DownloadItemRules? DownloadItemRule { get; set; } = null; public DownloadItemRules? DownloadItemRule { get; set; } = null;

View File

@ -403,21 +403,17 @@ LEFT JOIN replied_to rt ON m.message_id = rt.message_id" + filter.GenerateWhereC
} }
public void RemoveMessages(MessageFilter filter, FilterRemovalMode mode) { public void RemoveMessages(MessageFilter filter, FilterRemovalMode mode) {
var whereClause = filter.GenerateWhereClause(invert: mode == FilterRemovalMode.KeepMatching);
if (!string.IsNullOrEmpty(whereClause)) {
var perf = log.Start(); var perf = log.Start();
DeleteFromTable("messages", whereClause); DeleteFromTable("messages", filter.GenerateWhereClause(invert: mode == FilterRemovalMode.KeepMatching));
totalMessagesComputer.Recompute(); totalMessagesComputer.Recompute();
perf.End(); perf.End();
} }
}
public int CountAttachments(AttachmentFilter? filter = null) { public int CountAttachments(AttachmentFilter? filter = null) {
using var conn = pool.Take(); using var conn = pool.Take();
using var cmd = conn.Command("SELECT COUNT(*) FROM attachments a" + filter.GenerateWhereClause("a")); using var cmd = conn.Command("SELECT COUNT(DISTINCT url) FROM attachments a" + filter.GenerateWhereClause("a"));
using var reader = cmd.ExecuteReader(); using var reader = cmd.ExecuteReader();
return reader.Read() ? reader.GetInt32(0) : 0; return reader.Read() ? reader.GetInt32(0) : 0;
@ -476,7 +472,7 @@ LEFT JOIN replied_to rt ON m.message_id = rt.message_id" + filter.GenerateWhereC
public void EnqueueDownloadItems(AttachmentFilter? filter = null) { public void EnqueueDownloadItems(AttachmentFilter? filter = null) {
using var conn = pool.Take(); using var conn = pool.Take();
using var cmd = conn.Command("INSERT INTO downloads (url, status, size) SELECT a.url, :enqueued, a.size FROM attachments a" + filter.GenerateWhereClause("a")); using var cmd = conn.Command("INSERT INTO downloads (url, status, size) SELECT a.url, :enqueued, MAX(a.size) FROM attachments a" + filter.GenerateWhereClause("a") + " GROUP BY a.url");
cmd.AddAndSet(":enqueued", SqliteType.Integer, (int) DownloadStatus.Enqueued); cmd.AddAndSet(":enqueued", SqliteType.Integer, (int) DownloadStatus.Enqueued);
cmd.ExecuteNonQuery(); cmd.ExecuteNonQuery();
} }
@ -502,16 +498,13 @@ LEFT JOIN replied_to rt ON m.message_id = rt.message_id" + filter.GenerateWhereC
} }
public void RemoveDownloadItems(DownloadItemFilter? filter, FilterRemovalMode mode) { public void RemoveDownloadItems(DownloadItemFilter? filter, FilterRemovalMode mode) {
var whereClause = filter.GenerateWhereClause(invert: mode == FilterRemovalMode.KeepMatching); DeleteFromTable("downloads", filter.GenerateWhereClause(invert: mode == FilterRemovalMode.KeepMatching));
totalDownloadsComputer.Recompute();
if (!string.IsNullOrEmpty(whereClause)) {
DeleteFromTable("downloads", whereClause);
}
} }
public DownloadStatusStatistics GetDownloadStatusStatistics() { public DownloadStatusStatistics GetDownloadStatusStatistics() {
static void LoadUndownloadedStatistics(ISqliteConnection conn, DownloadStatusStatistics result) { static void LoadUndownloadedStatistics(ISqliteConnection conn, DownloadStatusStatistics result) {
using var cmd = conn.Command("SELECT IFNULL(COUNT(filtered.size), 0), IFNULL(SUM(filtered.size), 0) FROM (SELECT DISTINCT a.url, a.size FROM attachments a WHERE a.url NOT IN (SELECT d.url FROM downloads d)) filtered"); using var cmd = conn.Command("SELECT IFNULL(COUNT(size), 0), IFNULL(SUM(size), 0) FROM (SELECT MAX(a.size) size FROM attachments a WHERE a.url NOT IN (SELECT d.url FROM downloads d) GROUP BY a.url)");
using var reader = cmd.ExecuteReader(); using var reader = cmd.ExecuteReader();
if (reader.Read()) { if (reader.Read()) {
@ -655,7 +648,7 @@ FROM downloads");
private long ComputeAttachmentStatistics() { private long ComputeAttachmentStatistics() {
using var conn = pool.Take(); using var conn = pool.Take();
return conn.SelectScalar("SELECT COUNT(*) FROM attachments") as long? ?? 0L; return conn.SelectScalar("SELECT COUNT(DISTINCT url) FROM attachments") as long? ?? 0L;
} }
private void UpdateAttachmentStatistics(long totalAttachments) { private void UpdateAttachmentStatistics(long totalAttachments) {