From 1eb03d2a9c5ba9ee1ce4dc49a4740bac05ffa106 Mon Sep 17 00:00:00 2001 From: danrahn Date: Sat, 9 Mar 2024 11:47:44 -0800 Subject: [PATCH] Hodgepodge of small fixes/adjustments * Better MarkerBreakdown JSDoc * Share logic between MarkerBreakdown.introBuckets() and creditsBuckets() * Use more efficient cloning in MarkerBreakdown.data() * Fix SVGHelper JSDoc * Try to workaround mobile browser address bar height styling issues * Use replaceWith instead of insertBefore/removeChild in BulkShiftOverlay * Update .dockerignore to ignore thumbnail cache folder * Add extensions.json to recommend installing eslint in vscode --- .dockerignore | 1 + .gitignore | 3 +- .vscode/extensions.json | 14 +++++++++ Client/Script/BulkShiftOverlay.js | 3 +- Client/Script/SVGHelper.js | 4 +-- Client/Style/style.css | 14 +++++---- Shared/MarkerBreakdown.js | 47 ++++++++++++++++++------------- 7 files changed, 56 insertions(+), 30 deletions(-) create mode 100644 .vscode/extensions.json diff --git a/.dockerignore b/.dockerignore index ee1c490..af09127 100644 --- a/.dockerignore +++ b/.dockerignore @@ -9,6 +9,7 @@ node_modules .git Backup +cache dist Logs Test diff --git a/.gitignore b/.gitignore index 39b7768..c96e2b8 100644 --- a/.gitignore +++ b/.gitignore @@ -2,8 +2,7 @@ node_modules/* # Ignore everything in .vscode, except launch.json # for running the program/tests in vscode. -.vscode/* -!.vscode/launch.json +.vscode/settings.json # Ignore the backup database Backup/* diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..ab81623 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,14 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. + // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp + + // List of extensions which should be recommended for users of this workspace. + "recommendations": [ + "dbaeumer.vscode-eslint", + + ], + // List of extensions recommended by VS Code that should not be recommended for users of this workspace. + "unwantedRecommendations": [ + + ] +} \ No newline at end of file diff --git a/Client/Script/BulkShiftOverlay.js b/Client/Script/BulkShiftOverlay.js index 0b087a6..8194d51 100644 --- a/Client/Script/BulkShiftOverlay.js +++ b/Client/Script/BulkShiftOverlay.js @@ -265,8 +265,7 @@ class BulkShiftOverlay { const container = $('#bulkActionContainer'); const currentNode = $('#resolveShiftMessage'); if (currentNode) { - container.insertBefore(node, currentNode); - container.removeChild(currentNode); + currentNode.replaceWith(node); return; } diff --git a/Client/Script/SVGHelper.js b/Client/Script/SVGHelper.js index a8d807c..99a2c33 100644 --- a/Client/Script/SVGHelper.js +++ b/Client/Script/SVGHelper.js @@ -43,13 +43,13 @@ function getPlaceholder() { /** * Cache of all icon's that we've retrieved to avoid asking the server * for the same icon over and over. - * @type {Map} */ const svgCache = new Map(); /** * Keeps track of in-progress requests so when we e.g. add 100 expand/collapse arrows * at the same time, we only ask the server for it once. - * @type {Map} */ + * @type {Map>} */ const svgFetchMap = new Map(); /** diff --git a/Client/Style/style.css b/Client/Style/style.css index fe12429..36d6149 100644 --- a/Client/Style/style.css +++ b/Client/Style/style.css @@ -9,12 +9,7 @@ body { color: var(--theme-primary); } -/* Force the vertical scrollbar to always be visible to avoid horizontal shifts when the scrollbar appears/ -* disappears. There are some behavioral tradeoffs here (content is slightly off-center when no scrollbar -* is visible, but the lack of shifting wins for me. Also, apply this to #plexFrame instead of body, because -* overriding the body itself will prevent scrolling overlays from having a visible scrollbar. */ #plexFrame { - overflow-y: scroll; height: 100vh; scrollbar-width: thin !important; } @@ -726,6 +721,15 @@ input:focus-visible { #topRightControls { right: 30px; } + + /* Force the vertical scrollbar to always be visible to avoid horizontal shifts when the scrollbar appears/ + * disappears. There are some behavioral tradeoffs here (content is slightly off-center when no scrollbar + * is visible, but the lack of shifting wins for me. Also, apply this to #plexFrame instead of body, because + * overriding the body itself will prevent scrolling overlays from having a visible scrollbar. Only apply it + * to "probably not a phone", since this causes issues with mobile browsers' auto-hiding address bar */ + #plexFrame { + overflow-y: scroll; + } } @media all and (max-width: 767px) { /* Probably a phone */ diff --git a/Shared/MarkerBreakdown.js b/Shared/MarkerBreakdown.js index 3031c2e..be5948b 100644 --- a/Shared/MarkerBreakdown.js +++ b/Shared/MarkerBreakdown.js @@ -20,6 +20,7 @@ class MarkerBreakdown { constructor() {} /** + * Retrieve the key representing the given marker number and type. * @param {number} delta * @param {string} markerType */ static deltaFromType(delta, markerType) { @@ -36,12 +37,14 @@ class MarkerBreakdown { } /** + * Return the number of markers represented by the given key. * @param {number} key */ static markerCountFromKey(key) { return (key >> CreditsShift) + (key & IntroMask); } /** + * Retrieve the key representing the given number of intros and credits. * @param {number} intros * @param {number} credits */ static keyFromMarkerCount(intros, credits) { @@ -59,9 +62,14 @@ class MarkerBreakdown { return this; } + /** + * Return the number of unique marker type combinations for this breakdown. + * E.g. 3 will be returned if 5 episodes have 1 intro and no credits, 10 have + * no intros and 1 credits, and 2 have no intros and no credits. */ buckets() { return Object.keys(this.#counts).filter(c => this.#counts[c] !== 0).length; } /** + * Retrieve consolidated buckets that don't differentiate between marker types. * @returns {MarkerBreakdownMap} */ collapsedBuckets() { /** @type {MarkerBreakdownMap} */ @@ -86,41 +94,39 @@ class MarkerBreakdown { } /** + * Return a breakdown only consisting of intro markers. * @returns {MarkerBreakdownMap} */ introBuckets() { - /** @type {MarkerBreakdownMap} */ - const collapsed = {}; - for (const [key, value] of Object.entries(this.#counts)) { - if (value === 0) { - continue; - } - - const introKey = this.#ic(key); - collapsed[introKey] ??= 0; - collapsed[introKey] += value; - } - - return collapsed; + return this.#buckets(this.#ic); } /** + * Return a breakdown only consisting of credits markers. * @returns {MarkerBreakdownMap} */ creditsBuckets() { + return this.#buckets(this.#cc); + } + + /** + * @param {(key: number|string) => number} keyFunc */ + #buckets(keyFunc) { const collapsed = {}; for (const [key, value] of Object.entries(this.#counts)) { if (value === 0) { continue; } - const creditsKey = this.#cc(key); - collapsed[creditsKey] ??= 0; - collapsed[creditsKey] += value; + const typeKey = keyFunc(key); + collapsed[typeKey] ??= 0; + collapsed[typeKey] += value; } return collapsed; } + /** Intro count from key */ #ic(v) { return +v & IntroMask; } + /** Credits count from key */ #cc(v) { return +v >> CreditsShift; } /** @@ -165,11 +171,14 @@ class MarkerBreakdown { return Object.entries(this.#counts).reduce((acc, kv) => acc + (this.#cc(kv[0]) > 0 ? kv[1] : 0), 0); } - /** @returns {MarkerBreakdownMap} */ + /** + * Retrieve the full breakdown that includes marker type info. + * @returns {MarkerBreakdownMap} */ data() { - // Create a copy by stringifying/serializing to prevent underlying data from being overwritten. + // Create a copy to prevent underlying data from being overwritten. + // Since it's just a number-to-number mapping, the spread operator is sufficient. this.#minify(); - return JSON.parse(JSON.stringify(this.#counts)); + return { ...this.#counts }; } /** Adjust the marker count for an episode that previously had `oldCount` markers