From c5b5b754e242cb5db0ae25c46826dccdd51ef6da Mon Sep 17 00:00:00 2001 From: Vendicated Date: Fri, 25 Nov 2022 15:59:47 +0100 Subject: [PATCH] CallTimer --- package.json | 1 + pnpm-lock.yaml | 2 + src/plugins/callTimer.ts | 116 +++++++++++++++++++++++++++++++++++++++ src/webpack/common.tsx | 2 + 4 files changed, 121 insertions(+) create mode 100644 src/plugins/callTimer.ts diff --git a/package.json b/package.json index f03266a..a168448 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "eslint-plugin-header": "^3.1.1", "eslint-plugin-simple-import-sort": "^8.0.0", "eslint-plugin-unused-imports": "^2.0.0", + "moment": "^2.29.4", "puppeteer-core": "^19.2.2", "standalone-electron-types": "^1.0.0", "type-fest": "^3.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b178f0b..ba97ffa 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,6 +17,7 @@ specifiers: eslint-plugin-simple-import-sort: ^8.0.0 eslint-plugin-unused-imports: ^2.0.0 fflate: ^0.7.4 + moment: ^2.29.4 puppeteer-core: ^19.2.2 standalone-electron-types: ^1.0.0 type-fest: ^3.1.0 @@ -41,6 +42,7 @@ devDependencies: eslint-plugin-header: 3.1.1_eslint@8.24.0 eslint-plugin-simple-import-sort: 8.0.0_eslint@8.24.0 eslint-plugin-unused-imports: 2.0.0_5yz3upex2kb6hbdwaq7bihlxnq + moment: 2.29.4 puppeteer-core: 19.2.2 standalone-electron-types: 1.0.0 type-fest: 3.1.0 diff --git a/src/plugins/callTimer.ts b/src/plugins/callTimer.ts new file mode 100644 index 0000000..d58b428 --- /dev/null +++ b/src/plugins/callTimer.ts @@ -0,0 +1,116 @@ +/* + * Vencord, a modification for Discord's desktop app + * Copyright (c) 2022 Vendicated and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . +*/ + +import { Settings } from "../api/settings"; +import { Devs } from "../utils/constants"; +import definePlugin, { OptionType } from "../utils/types"; +import { FluxDispatcher } from "../webpack/common"; + +export default definePlugin({ + name: "CallTimer", + description: "Adds a timer to vcs", + authors: [Devs.Ven], + + style: void 0 as HTMLStyleElement | undefined, + startTime: 0, + interval: void 0 as NodeJS.Timeout | undefined, + + options: { + format: { + type: OptionType.SELECT, + description: "The timer format. This can be any valid moment.js format", + options: [ + { + label: "30d 23:00:42", + value: "stopwatch", + default: true + }, + { + label: "30d 23h 00m 42s", + value: "human" + } + ] + } + }, + + + formatDuration(ms: number) { + // here be dragons (moment fucking sucks) + const human = Settings.plugins.CallTimer.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; + }, + + setTimer(ms: number) { + if (!this.style) return; + + this.style.textContent = ` + [class*="connection-"] [class*="channel-"]::after { + content: "Connected for ${this.formatDuration(ms)}"; + display: block; + } + `; + }, + + start() { + const style = this.style = document.createElement("style"); + style.id = "VencordCallTimer"; + document.head.appendChild(style); + + this.setTimer(0); + + this.handleRtcConnectionState = this.handleRtcConnectionState.bind(this); + FluxDispatcher.subscribe("RTC_CONNECTION_STATE", this.handleRtcConnectionState); + }, + + handleRtcConnectionState(e: { state: string; }) { + if (e.state === "RTC_CONNECTED" || e.state === "RTC_DISCONNECTED") { + clearInterval(this.interval); + if (e.state === "RTC_CONNECTED") { + this.startTime = Date.now(); + this.interval = setInterval( + () => this.setTimer(Math.round(Date.now() - this.startTime)), + 1000 + ); + } else this.startTime = 0; + this.setTimer(0); + } + }, + + stop() { + FluxDispatcher.unsubscribe("RCT_CONNECTION_STATE", this.handleRtcConnectionState); + this.style?.remove(); + clearInterval(this.interval); + } +}); diff --git a/src/webpack/common.tsx b/src/webpack/common.tsx index fa6bb0e..6ebad16 100644 --- a/src/webpack/common.tsx +++ b/src/webpack/common.tsx @@ -32,6 +32,8 @@ export const Flux = lazyWebpack(filters.byProps("connectStores")); export let React: typeof import("react"); export const ReactDOM: typeof import("react-dom") = lazyWebpack(filters.byProps("createPortal", "render")); +export const moment: typeof import("moment") = lazyWebpack(filters.byProps("parseTwoDigitYear")); + export const MessageStore = lazyWebpack(filters.byProps("getRawMessages")) as Omit & { getMessages(chanId: string): any; }; export const PermissionStore = lazyWebpack(filters.byProps("can", "getGuildPermissions")); export const PrivateChannelsStore = lazyWebpack(filters.byProps("openPrivateChannel"));