SpotifyControls: improve time formatting for tracks over an hour

This commit is contained in:
Vendicated 2026-07-26 18:06:28 +02:00
parent 0f10b57065
commit a040c8dbe6
No known key found for this signature in database
GPG key ID: D66986BAF75ECF18
4 changed files with 41 additions and 40 deletions

View file

@ -20,6 +20,7 @@ import { definePluginSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants";
import { useTimer } from "@utils/react";
import { formatDuration } from "@utils/text";
import definePlugin, { OptionType } from "@utils/types";
import { React } from "@webpack/common";
@ -39,33 +40,10 @@ const settings = definePluginSettings({
label: "30d 23h 00m 42s",
value: "human"
}
]
] as const
}
});
function formatDuration(ms: number) {
// here be dragons (moment fucking sucks)
const human = settings.store.format === "human";
const format = (n: number) => human ? n : n.toString().padStart(2, "0");
const unit = (s: string) => human ? s : "";
const delim = human ? " " : ":";
// thx copilot
const d = Math.floor(ms / 86400000);
const h = Math.floor((ms % 86400000) / 3600000);
const m = Math.floor(((ms % 86400000) % 3600000) / 60000);
const s = Math.floor((((ms % 86400000) % 3600000) % 60000) / 1000);
let res = "";
if (d) res += `${d}d `;
if (h || res) res += `${format(h)}${unit("h")}${delim}`;
if (m || res || !human) res += `${format(m)}${unit("m")}${delim}`;
res += `${format(s)}${unit("s")}`;
return res;
}
export default definePlugin({
@ -94,7 +72,7 @@ export default definePlugin({
return (
<p style={{ margin: 0, fontFamily: "var(--font-code)" }}>
{formatDuration(time)}
{formatDuration(time, settings.store.format === "human")}
</p>
);
}, { noop: true }),

View file

@ -22,7 +22,7 @@ import PermissionsViewerPlugin from "@plugins/permissionsViewer";
import openRolesAndUsersPermissionsModal from "@plugins/permissionsViewer/components/RolesAndUsersPermissions";
import { sortPermissionOverwrites } from "@plugins/permissionsViewer/utils";
import { classes } from "@utils/misc";
import { formatDuration } from "@utils/text";
import { formatDurationVerbose } from "@utils/text";
import type { Channel, RoleOrUserPermission } from "@vencord/discord-types";
import { findByPropsLazy, findComponentByCodeLazy, findCssClassesLazy } from "@webpack";
import { EmojiStore, FluxDispatcher, GuildMemberStore, GuildStore, Parser, PermissionsBits, PermissionStore, SnowflakeUtils, Text, Timestamp, Tooltip, useEffect, useState } from "@webpack/common";
@ -204,11 +204,11 @@ function HiddenChannelLockScreen({ channel }: { channel: Channel; }) {
<Text variant="text-md/normal">Last message pin: <Timestamp timestamp={new Date(lastPinTimestamp)} /></Text>
}
{(rateLimitPerUser ?? 0) > 0 &&
<Text variant="text-md/normal">Slowmode: {formatDuration(rateLimitPerUser!, "seconds")}</Text>
<Text variant="text-md/normal">Slowmode: {formatDurationVerbose(rateLimitPerUser!, "seconds")}</Text>
}
{(defaultThreadRateLimitPerUser ?? 0) > 0 &&
<Text variant="text-md/normal">
Default thread slowmode: {formatDuration(defaultThreadRateLimitPerUser!, "seconds")}
Default thread slowmode: {formatDurationVerbose(defaultThreadRateLimitPerUser!, "seconds")}
</Text>
}
{((channel.isGuildVoice() || channel.isGuildStageVoice()) && bitrate != null) &&
@ -223,7 +223,7 @@ function HiddenChannelLockScreen({ channel }: { channel: Channel; }) {
{(defaultAutoArchiveDuration ?? 0) > 0 &&
<Text variant="text-md/normal">
Default inactivity duration before archiving {channel.isForumChannel() ? "posts" : "threads"}:
{" " + formatDuration(defaultAutoArchiveDuration!, "minutes")}
{" " + formatDurationVerbose(defaultAutoArchiveDuration!, "minutes")}
</Text>
}
{defaultForumLayout != null &&

View file

@ -26,6 +26,7 @@ import { debounce } from "@shared/debounce";
import { classNameFactory } from "@utils/css";
import { copyWithToast, openImageModal } from "@utils/discord";
import { classes } from "@utils/misc";
import { formatDuration } from "@utils/text";
import { ContextMenuApi, FluxDispatcher, Menu, React, useEffect, useState, useStateFromStores } from "@webpack/common";
import { settings } from ".";
@ -34,13 +35,6 @@ import { SpotifyStore, Track } from "./SpotifyStore";
const cl = classNameFactory("vc-spotify-");
function msToHuman(ms: number) {
const minutes = ms / 1000 / 60;
const m = Math.floor(minutes);
const s = Math.floor((minutes - m) * 60);
return `${m.toString().padStart(2, "0")}:${s.toString().padStart(2, "0")}`;
}
function Svg(path: string, label: string) {
return () => (
<svg
@ -193,7 +187,7 @@ function SpotifySeekBar() {
className={cl("progress-time") + " " + cl("time-left")}
aria-label="Progress"
>
{msToHuman(position)}
{formatDuration(position)}
</Span>
<SeekBar
initialValue={position}
@ -201,7 +195,7 @@ function SpotifySeekBar() {
maxValue={duration}
onValueChange={onChange}
asValueChanges={onChange}
onValueRender={msToHuman}
onValueRender={formatDuration}
/>
<Span
size="xs"
@ -209,7 +203,7 @@ function SpotifySeekBar() {
className={cl("progress-time") + " " + cl("time-right")}
aria-label="Total Duration"
>
{msToHuman(duration)}
{formatDuration(duration)}
</Span>
</div>
);

View file

@ -16,6 +16,8 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { isTruthy } from "./guards";
// Utils for readable text transformations eg: `toTitle(fromKebab())`
// Case style to words
@ -51,7 +53,7 @@ function getUnitStr(unit: Units, isOne: boolean, short: boolean) {
* @param unit The unit the time is on
* @param short Whether to use short units like "d" instead of "days"
*/
export function formatDuration(time: number, unit: Units, short: boolean = false) {
export function formatDurationVerbose(time: number, unit: Units, short: boolean = false) {
const { moment } = require("@webpack/common") as typeof import("@webpack/common");
const dur = moment.duration(time, unit);
@ -93,6 +95,33 @@ export function formatDuration(time: number, unit: Units, short: boolean = false
return res.length ? res : `0 ${getUnitStr(unit, false, short)}`;
}
export function formatDuration(ms: number, human = false) {
const DAY = 86_400_000;
const HOUR = 3_600_000;
const MINUTE = 60_000;
const days = Math.floor(ms / DAY);
const hours = Math.floor((ms % DAY) / HOUR);
const minutes = Math.floor((ms % HOUR) / MINUTE);
const seconds = Math.floor((ms % MINUTE) / 1000);
const pad = (n: number) => human ? n : n.toString().padStart(2, "0");
const unit = (s: string) => human ? s : "";
const showDays = days !== 0;
const showHours = showDays || hours !== 0;
const showMinutes = showHours || minutes !== 0 || !human;
const duration = [
showHours && `${pad(hours)}${unit("h")}`,
showMinutes && `${pad(minutes)}${unit("m")}`,
`${pad(seconds)}${unit("s")}`
].filter(isTruthy).join(human ? " " : ":");
const prefix = showDays ? `${days}d ` : "";
return prefix + duration;
}
/**
* Join an array of strings in a human readable way (1, 2 and 3)
* @param elements Elements