diff --git a/src/equicordplugins/autoJump/README.md b/src/equicordplugins/autoJump/README.md deleted file mode 100644 index e361254ca..000000000 --- a/src/equicordplugins/autoJump/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# AutoJump - -Jumps to the first message in new messages for a channel diff --git a/src/equicordplugins/autoJump/index.tsx b/src/equicordplugins/autoJump/index.tsx deleted file mode 100644 index b05ba1e64..000000000 --- a/src/equicordplugins/autoJump/index.tsx +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2024 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -import { NavContextMenuPatchCallback } from "@api/ContextMenu"; -import { definePluginSettings } from "@api/Settings"; -import { EquicordDevs } from "@utils/constants"; -import definePlugin, { OptionType } from "@utils/types"; -import { ChannelStore, Menu, MessageActions, NavigationRouter } from "@webpack/common"; - -interface ChannelSelectEvent { - type: "CHANNEL_SELECT"; - channelId: string | null; - guildId: string | null; -} - -let lastChannelId = "0"; - -function autoJump({ guild_id, id: channelId }) { - const guildId = guild_id ?? "@me"; - - lastChannelId = channelId; - NavigationRouter.transitionTo(`/channels/${guildId}/${channelId}`); - MessageActions.jumpToPresent(channelId, { limit: null }); -} - -const MenuPatch: NavContextMenuPatchCallback = (children, { channel }) => { - children.push( - { - autoJump(channel); - }} - /> - ); -}; - -const settings = definePluginSettings({ - autoJumping: { - type: OptionType.BOOLEAN, - description: "Automatically jump to the last message in the channel when switching channels", - default: false - } -}); - -export default definePlugin({ - name: "AutoJump", - description: "Jumps to Last Message in Channel", - authors: [EquicordDevs.omaw], - settings, - contextMenus: { - "channel-context": MenuPatch, - "user-context": MenuPatch, - "thread-context": MenuPatch - }, - flux: { - async CHANNEL_SELECT({ guildId, channelId }: ChannelSelectEvent) { - if (!settings.store.autoJumping || !channelId) return; - - const channel = ChannelStore.getChannel(channelId); - if (!channel || channel.id === lastChannelId) return; - - autoJump({ guild_id: guildId, id: channelId }); - } - } -}); diff --git a/src/equicordplugins/channelTabs/index.tsx b/src/equicordplugins/channelTabs/index.tsx index 2a693b24a..b699f2d21 100644 --- a/src/equicordplugins/channelTabs/index.tsx +++ b/src/equicordplugins/channelTabs/index.tsx @@ -80,8 +80,8 @@ export default definePlugin({ { find: "(this,\"handleMessageClick\"", replacement: { - match: /(?<=(\i)\.isSearchHit\));(?=null!=(\i))/, - replace: ";if ($1.ctrlKey) return $self.open($2);" + match: /(\i)\.stopPropagation.{0,50}(?=null!=(\i))/, + replace: "$&if ($1.ctrlKey) return $self.open($2);" } }, // prevent issues with the pins/inbox popouts being too tall diff --git a/src/equicordplugins/chatButtonsPlus/index.tsx b/src/equicordplugins/chatButtonsPlus/index.tsx index 3634d67d1..5a8836acf 100644 --- a/src/equicordplugins/chatButtonsPlus/index.tsx +++ b/src/equicordplugins/chatButtonsPlus/index.tsx @@ -8,7 +8,7 @@ import "./styles.css"; import { ChatBarButton } from "@api/ChatButtons"; import { DataStore } from "@api/index"; -import { definePluginSettings, Settings } from "@api/Settings"; +import { definePluginSettings, migratePluginSettings, Settings } from "@api/Settings"; import { EquicordDevs } from "@utils/constants"; import { getCurrentChannel, sendMessage } from "@utils/discord"; import { useForceUpdater } from "@utils/react"; @@ -223,6 +223,7 @@ const settings = definePluginSettings({ } }); +migratePluginSettings("ChatButtonsPlus", "Meow", "Woof"); export default definePlugin({ name: "ChatButtonsPlus", description: "Add custom chat buttons with personalized + messages and SVG icons", diff --git a/src/equicordplugins/customTimestamps/index.tsx b/src/equicordplugins/customTimestamps/index.tsx index 087e36700..f9eab235d 100644 --- a/src/equicordplugins/customTimestamps/index.tsx +++ b/src/equicordplugins/customTimestamps/index.tsx @@ -7,12 +7,14 @@ import "./style.css"; import { definePluginSettings, useSettings } from "@api/Settings"; +import ErrorBoundary from "@components/ErrorBoundary"; import { Link } from "@components/Link"; import { Devs, EquicordDevs } from "@utils/constants"; import { Margins } from "@utils/margins"; import { useForceUpdater } from "@utils/react"; import definePlugin, { OptionType } from "@utils/types"; -import { Forms, moment, TextInput, useEffect, useState } from "@webpack/common"; +import { findByCodeLazy, findComponentByCodeLazy } from "@webpack"; +import { Forms, moment, TextInput, useEffect, useRef, UserStore, useState } from "@webpack/common"; type TimeFormat = { name: string; @@ -66,9 +68,9 @@ const timeFormats: Record = { }, lastWeekFormat: { name: "Last week", - description: "[calendar] format for last week", + description: "[calendar] format for within the last week", default: "ddd DD.MM.YYYY HH:mm:ss", - offset: -1000 * 60 * 60 * 24 * 7, + offset: -1000 * 60 * 60 * 24 * 6, // setting an offset of a week exactly pushes it into "older date" territory as soon as a second passes }, sameElseFormat: { name: "Older date", @@ -102,31 +104,80 @@ const format = (date: Date, formatTemplate: string): string => { const TimeRow = (props: TimeRowProps) => { const [state, setState] = useState(props.pluginSettings?.[props.id] || props.format.default); - const [preview, setPreview] = useState(""); const handleChange = (value: string) => { setState(value); props.onChange(props.id, value); }; - const updatePreview = () => setPreview(format(new Date(Date.now() + props.format.offset), state || props.format.default)); - - useEffect(() => { - updatePreview(); - const interval = setInterval(updatePreview, 1000); - return () => clearInterval(interval); - }, [state]); - return ( <> {props.format.name} {props.format.description} - {preview} ); }; +const MessagePreview = findComponentByCodeLazy<{ + author: any, + message: any, + compact: boolean, + isGroupStart: boolean, + className: string, + hideSimpleEmbedContent: boolean; +}>(/previewGuildId:\i,preview:\i,/); +const createBotMessage = findByCodeLazy('username:"Clyde"'); +const populateMessagePrototype = findByCodeLazy("isProbablyAValidSnowflake", "messageReference:"); + +const DemoMessage = (props: { msgId, compact, message, date: Date | undefined, isGroupStart?: boolean; }) => { + const message = createBotMessage({ content: props.message, channelId: "1337", embeds: [] }); + message.author = UserStore.getCurrentUser(); + message.id = props.msgId; + message.timestamp = moment(props.date ?? new Date()); + const user = UserStore.getCurrentUser(); + const populatedMessage = message && populateMessagePrototype(message); + return populatedMessage ? ( +
+ +
+ ) :
+ + {/* @ts-ignore */} + Preview: {Vencord.Plugins.plugins.CustomTimestamps.renderTimestamp(date, "cozy")} + +
; +}; + +const DemoMessageContainer = ErrorBoundary.wrap(() => { + const [isCompact, setIsCompact] = useState(false); + const today = useRef(new Date()); + const yesterday = useRef(new Date(Date.now() + timeFormats.lastDayFormat.offset)); + const lastWeek = useRef(new Date(Date.now() + timeFormats.lastWeekFormat.offset)); + const aMonthAgo = useRef(new Date(Date.now() + timeFormats.sameElseFormat.offset)); + + return ( +
setIsCompact(!isCompact)}> + + + + +
+ ); +}, { noop: true }); + const settings = definePluginSettings({ formats: { type: OptionType.COMPONENT, @@ -140,25 +191,29 @@ const settings = definePluginSettings({ componentProps.setValue(newSettings); }; - return Object.entries(timeFormats).map(([key, value]) => ( - - {key === "sameDayFormat" && ( -
- - Calendar formats - - How to format the [calendar] value if used in the above timestamps. - -
- )} - -
- )); + return ( + <> + + {Object.entries(timeFormats).map(([key, value]) => ( + + {key === "sameDayFormat" && ( +
+ + Calendar formats + + How to format the [calendar] value if used in the above timestamps. + +
+ )} + +
+ ))} + ); } } }).withPrivateSettings<{ @@ -230,16 +285,16 @@ export default definePlugin({ switch (type) { case "cozy": - formatTemplate = settings.use(["formats"]).formats?.cozyFormat || timeFormats.cozyFormat.default; + formatTemplate = settings.store.formats?.cozyFormat || timeFormats.cozyFormat.default; break; case "compact": - formatTemplate = settings.use(["formats"]).formats?.compactFormat || timeFormats.compactFormat.default; + formatTemplate = settings.store.formats?.compactFormat || timeFormats.compactFormat.default; break; case "tooltip": - formatTemplate = settings.use(["formats"]).formats?.tooltipFormat || timeFormats.tooltipFormat.default; + formatTemplate = settings.store.formats?.tooltipFormat || timeFormats.tooltipFormat.default; break; case "ariaLabel": - formatTemplate = settings.use(["formats"]).formats?.ariaLabelFormat || timeFormats.ariaLabelFormat.default; + formatTemplate = settings.store.formats?.ariaLabelFormat || timeFormats.ariaLabelFormat.default; } useEffect(() => { diff --git a/src/equicordplugins/customTimestamps/style.css b/src/equicordplugins/customTimestamps/style.css index 0314d70ab..b6102466c 100644 --- a/src/equicordplugins/customTimestamps/style.css +++ b/src/equicordplugins/customTimestamps/style.css @@ -6,7 +6,12 @@ margin: 10px 0; } -.vc-cmt-preview-text { - color: yellow; - margin: 8px 0; +.vc-cmt-demo-message-container{ + position: sticky; + background: var(--background-base-lowest); + padding: 10px 0; + cursor: pointer; + top: -8px; + border: solid grey 1px; + border-radius: 5px; } diff --git a/src/equicordplugins/equicordToolbox/index.tsx b/src/equicordplugins/equicordToolbox/index.tsx index ea175d513..84b8c6884 100644 --- a/src/equicordplugins/equicordToolbox/index.tsx +++ b/src/equicordplugins/equicordToolbox/index.tsx @@ -152,9 +152,10 @@ export default definePlugin({ patches: [ { - find: '"M9 3v18"', + find: '?"BACK_FORWARD_NAVIGATION":', replacement: { - match: /focusSectionProps:"HELP".{0,20},className:(\i\.button)\}\),/, + // TODO: (?:\.button) is for stable compat and should be removed soon:tm: + match: /focusSectionProps:"HELP".{0,20},className:(\i(?:\.button)?)\}\),/, replace: "$& $self.renderVencordPopoutButton($1)," } } diff --git a/src/equicordplugins/inviteDefaults/index.ts b/src/equicordplugins/inviteDefaults/index.ts index 1cb5baf1d..288622324 100644 --- a/src/equicordplugins/inviteDefaults/index.ts +++ b/src/equicordplugins/inviteDefaults/index.ts @@ -52,7 +52,7 @@ export default definePlugin({ find: ".GUILD_CREATE_INVITE_SUGGESTION,defaultMaxAge", replacement: [ { - match: /(?<=maxAge:)null!=\(\i=null!=\i\?\i:\i\)\?\i:\i.value/, + match: /(?<=maxAge:)null!=\(\i=null!=\i\?\i:\i\)\?\i:\i.\i/, replace: "$self.settings.store.inviteDuration" }, { diff --git a/src/equicordplugins/mediaPlaybackSpeed/components/SpeedIcon.tsx b/src/equicordplugins/mediaPlaybackSpeed/components/SpeedIcon.tsx index c6d4a907d..b23a358bb 100644 --- a/src/equicordplugins/mediaPlaybackSpeed/components/SpeedIcon.tsx +++ b/src/equicordplugins/mediaPlaybackSpeed/components/SpeedIcon.tsx @@ -13,7 +13,7 @@ function SpeedIcon() { fill="currentColor" viewBox="0 -960 960 960" > - + ); } diff --git a/src/equicordplugins/mediaPlaybackSpeed/index.tsx b/src/equicordplugins/mediaPlaybackSpeed/index.tsx index b2de586c1..39aa162aa 100644 --- a/src/equicordplugins/mediaPlaybackSpeed/index.tsx +++ b/src/equicordplugins/mediaPlaybackSpeed/index.tsx @@ -11,7 +11,7 @@ import { classNameFactory } from "@api/Styles"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import definePlugin, { makeRange, OptionType } from "@utils/types"; -import { ContextMenuApi, FluxDispatcher, Heading, Menu, React, Tooltip, useEffect } from "@webpack/common"; +import { ContextMenuApi, FluxDispatcher, Menu, React, Tooltip, useEffect } from "@webpack/common"; import { RefObject } from "react"; import SpeedIcon from "./components/SpeedIcon"; @@ -23,15 +23,6 @@ const max = 3.5; const speeds = makeRange(min, max, 0.25); const settings = definePluginSettings({ - test: { - type: OptionType.COMPONENT, - description: "", - component() { - return - Default playback speeds - ; - } - }, defaultVoiceMessageSpeed: { type: OptionType.SLIDER, default: 1, @@ -61,7 +52,7 @@ export default definePlugin({ settings, - PlaybackSpeedComponent({ mediaRef }: { mediaRef: MediaRef; }) { + renderPlaybackSpeedComponent: ErrorBoundary.wrap(({ mediaRef }: { mediaRef: MediaRef; }) => { const changeSpeed = (speed: number) => { const media = mediaRef?.current; if (media) { @@ -70,11 +61,16 @@ export default definePlugin({ }; useEffect(() => { - if (!mediaRef?.current) return; - const media = mediaRef.current; + const media = mediaRef?.current; + if (!media) return; if (media.tagName === "AUDIO") { const isVoiceMessage = media.className.includes("audioElement_"); - changeSpeed(isVoiceMessage ? settings.store.defaultVoiceMessageSpeed : settings.store.defaultAudioSpeed); + if (isVoiceMessage) { + // Workaround because Discord seems to override it somewhere + media.addEventListener("play", () => { changeSpeed(settings.store.defaultVoiceMessageSpeed); }, { once: true }); + } else { + changeSpeed(settings.store.defaultAudioSpeed); + } } else if (media.tagName === "VIDEO") { changeSpeed(settings.store.defaultVideoSpeed); } @@ -113,21 +109,15 @@ export default definePlugin({ )} ); - }, - - renderComponent(mediaRef: MediaRef) { - return - - ; - }, + }), patches: [ - // voice message embeds + // replace voice message embed speed control because ours provides more speeds { find: "\"--:--\"", replacement: { - match: /onVolumeShow:\i,onVolumeHide:\i\}\)(?<=useCallback\(\(\)=>\{let \i=(\i).current;.+?)/, - replace: "$&,$self.renderComponent($1)" + match: /\(0,\i\.jsxs?\)\(.{0,50}\.playbackRateContainer.+?}\)}\)(?<=playbackCacheKey:\i\}=\i,(\i).+?)/, + replace: "$self.renderPlaybackSpeedComponent({mediaRef:$1})" } }, // audio & video embeds @@ -142,8 +132,8 @@ export default definePlugin({ { find: "AUDIO:\"AUDIO\"", replacement: { - match: /onVolumeHide:\i,iconClassName:\i.controlIcon,iconColor:"currentColor",sliderWrapperClassName:\i.volumeSliderWrapper\}\)\}\),/, - replace: "$&$self.renderComponent(this.props.mediaRef)," + match: /\i.volumeSliderWrapper\}\)\}\),/, + replace: "$&$self.renderPlaybackSpeedComponent({mediaRef:this?.props?.mediaRef})," } } ] diff --git a/src/equicordplugins/mediaPlaybackSpeed/styles.css b/src/equicordplugins/mediaPlaybackSpeed/styles.css index 816190a48..e14279a86 100644 --- a/src/equicordplugins/mediaPlaybackSpeed/styles.css +++ b/src/equicordplugins/mediaPlaybackSpeed/styles.css @@ -7,4 +7,4 @@ .vc-media-playback-speed-icon:hover { color: var(--interactive-active); -} +} \ No newline at end of file diff --git a/src/equicordplugins/rpcEditor/index.tsx b/src/equicordplugins/rpcEditor/index.tsx index 59c56ec78..b623caacf 100644 --- a/src/equicordplugins/rpcEditor/index.tsx +++ b/src/equicordplugins/rpcEditor/index.tsx @@ -109,7 +109,7 @@ export default definePlugin({ { find: '"LocalActivityStore"', replacement: { - match: /\i\(\i\)\{.{0,25}activity:(\i)\}=\i;/, + match: /\i\(\i\)\{.{0,25}activity:(\i).*?\}=\i;/, replace: "$&$self.patchActivity($1);", } } diff --git a/src/equicordplugins/showMessageEmbeds/index.tsx b/src/equicordplugins/showMessageEmbeds/index.tsx new file mode 100644 index 000000000..d224923d3 --- /dev/null +++ b/src/equicordplugins/showMessageEmbeds/index.tsx @@ -0,0 +1,212 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { findGroupChildrenByChildId } from "@api/ContextMenu"; +import { updateMessage } from "@api/MessageUpdater"; +import { ImageInvisible, ImageVisible } from "@components/Icons"; +import { EquicordDevs } from "@utils/constants"; +import { Logger } from "@utils/Logger"; +import { parseUrl } from "@utils/misc"; +import definePlugin from "@utils/types"; +import { Message } from "@vencord/discord-types"; +import { findByCodeLazy } from "@webpack"; +import { ChannelStore, Constants, Menu, MessageStore, React, RestAPI, showToast, Toasts } from "@webpack/common"; + +const logger = new Logger("ShowMessageEmbeds"); + +const addShowEmbedButton = (children, props) => { + if (props.itemSrc || !props.itemHref || !props.message) return; // itemSrc means the right clicked item is an image/attachment + + const group = findGroupChildrenByChildId("copy-native-link", children); + if (!group) return; + + const { message } = props; + + addButton(group, message, props.itemHref); +}; + +const addShowAttachmentEmbedButton = (children, props) => { + if (!props.attachmentUrl) return; + + const message = MessageStore.getMessage(props.channelId, props.messageId); + if (!message) return; + + addButton(children, message, props.attachmentUrl); +}; + +const addButton = (children, message, url) => { + url = normaliseUrl(url); + + if (!isEmbedInMessage(message, url)) { + children.splice(0, 0, + unfurlEmbed(url, message)} + icon={ImageVisible} + key="vc-sme-show" />); + } else if (isUrlInMessage(message, url)) { // check the url is actually in the message text so we know it's one people can actually add back + children.splice(0, 0, + removeEmbed(url, message)} + icon={ImageInvisible} + key="vc-sme-remove" />); + } +}; + +function isEmbedInMessage(message: Message, url: string): boolean { + return message?.embeds?.some((embed: any) => { + return embed?.url === url; + }) || message?.attachments?.some((attachment: any) => { + return attachment?.url === url; + }); +} + +function isUrlInMessage(message: Message, url: string): boolean { + const urls = message?.content?.match(/https?:\/\/[^\s]+/g) || []; + for (const u of urls) { + if (u === url || normaliseUrl(u) === url) { + return true; + } + } + for (const embed of message.embeds) { + // check if the embed content contains the url but not the embed.url property itself + if (embed.url !== url && (embed.rawDescription?.includes(url) || embed.fields.some((field: any) => field.value.includes(url)))) { + return true; + } + } + return false; +} + +// special cases where the unfurl api endpoint returns an embed with a different url than the one we requested +// e.g. you request an embed for a youtu.be link and the returned object has { ... url: youtube.com } +// this is not an exhaustive list, may need to add more cases in the future +function normaliseUrl(url: string): string { + // normalise youtube urls to the /watch?v= format (t param is replaced with start, v always comes first) + const youtubeRegex = /(https?:\/\/)?(?:m\.|www\.)?(youtu\.be|youtube\.com)\/(embed\/|v\/|watch\?v=|watch\?.+&v=|shorts\/)?((\w|-){11})(?:\S+)?/; + + if (youtubeRegex.test(url)) { + const urlObj = new URL(url); + const params = new URLSearchParams(urlObj.search); + let start = 0; + + if (params.has("t") || params.has("start")) { + const startParam = params.get("start") || params.get("t"); + if (startParam && startParam.match(/^(?:(\d+h)?(\d+m)?(\d+s)?(\d+)?)?$/)) { + const hours = startParam.match(/(\d+)h/); + const minutes = startParam.match(/(\d+)m/); + const seconds = startParam.match(/(\d+)s/); + const unqualifiedSeconds = startParam.match(/(\d+)$/); + if (hours) start += parseInt(hours[1]) * 3600; + if (minutes) start += parseInt(minutes[1]) * 60; + if (seconds) start += parseInt(seconds[1]); + if (unqualifiedSeconds) start += parseInt(unqualifiedSeconds[1]); + } + } + url = url.replace(youtubeRegex, `https://www.youtube.com/watch?v=$4${start ? "&start=" + start : ""}`); + } + + // x.com links still return an embed with url twitter.com at the moment + const xDotComRegex = /(https?:\/\/(?:www\.)?)x\.com(\/.*)?/; + + if (xDotComRegex.test(url)) { + url = url.replace(xDotComRegex, (match, p1, p2) => p1 + "twitter.com" + (p2 || "")); + } + + // tiktok adds ?enable_tiktok_webview=true + const tiktokRegex = /(https?:\/\/(?:www\.)?)tiktok\.com(\/.*)?/; + const searchParams = url.includes("?") ? new URLSearchParams(url.split("?")[1]) : new URLSearchParams(); + if (tiktokRegex.test(url) && !searchParams.has("enable_tiktok_webview")) { + searchParams.append("enable_tiktok_webview", "true"); + const newSearch = searchParams.toString(); + url = url.split("?")[0] + (newSearch ? "?" + newSearch : ""); + } + + return url; +} + +const convertEmbed = findByCodeLazy(".uniqueId(\"embed_\")"); + +async function unfurlEmbed(url: string, message: Message) { + const channel = ChannelStore.getChannel(message.channel_id); + + if (!parseUrl(url)) { + return; + } + + const resp = await RestAPI.post({ + url: Constants.Endpoints.UNFURL_EMBED_URLS, + body: { + urls: [url] + } + }).catch(e => { + showFailureToast("Failed to get embed"); + logger.error("Failed to get embed", e); + }); + + if (!resp?.body?.embeds || resp.body.embeds.length === 0) { + showFailureToast("No embeds found"); + return; + } + + const { embeds } = resp.body; + const convertedEmbeds: any = []; + + for (const embed of embeds) { + try { + const convertedEmbed = convertEmbed(channel.id, message.id, embed); + if (!convertedEmbed) { + showFailureToast("Failed to get embed"); + logger.error("embed object couldn't be converted", embed); + continue; + } + convertedEmbeds.push(convertedEmbed); + } catch (e) { + showFailureToast("Failed to get embed"); + logger.error("Failed to convert embed", e); + } + } + + const newEmbeds = [...message.embeds, ...convertedEmbeds]; + + newEmbeds.sort((a: any, b: any) => { + return message.content.indexOf(a.url) - message.content.indexOf(b.url); + }); + + updateMessage(message.channel_id, message.id, { embeds: newEmbeds }); +} + +function removeEmbed(url: string, message: Message) { + const newEmbeds = message.embeds.filter((embed: any) => embed.url !== url); + updateMessage(message.channel_id, message.id, { embeds: newEmbeds }); +} + +function showFailureToast(message: string) { + showToast(message, Toasts.Type.FAILURE, { position: Toasts.Position.BOTTOM }); +} + +export default definePlugin({ + name: "ShowMessageEmbeds", + description: "Adds a context menu option to show embeds for links that don't have one", + authors: [EquicordDevs.Suffocate], + + patches: [ + { + find: "className:\"attachmentLink\",", + replacement: { + match: /(?:(\i).noStyleAndInteraction.*?)attachmentName:\i.attachmentName/, + replace: "$&,channelId:$1.channelId,messageId:$1.messageId", + } + } + ], + + contextMenus: { + "message": addShowEmbedButton, + "attachment-link-context": addShowAttachmentEmbedButton + } +}); diff --git a/src/equicordplugins/spotifyActivityToggle/index.tsx b/src/equicordplugins/spotifyActivityToggle/index.tsx index a9f3b2831..ebee29f76 100644 --- a/src/equicordplugins/spotifyActivityToggle/index.tsx +++ b/src/equicordplugins/spotifyActivityToggle/index.tsx @@ -4,12 +4,11 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { definePluginSettings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { EquicordDevs } from "@utils/constants"; -import definePlugin, { OptionType } from "@utils/types"; +import definePlugin from "@utils/types"; import { findComponentByCodeLazy } from "@webpack"; -import { Constants, React, RestAPI } from "@webpack/common"; +import { Constants, React, RestAPI, useEffect, useState } from "@webpack/common"; const Button = findComponentByCodeLazy(".NONE,disabled:", ".PANEL_BUTTON"); @@ -35,55 +34,54 @@ function makeSpotifyIcon(enabled: boolean) { ); } -async function setSpotifyActivity(value: boolean) { - const { body } = await RestAPI.get({ - url: Constants.Endpoints.CONNECTIONS - }); - - if (!body) return; - const spotifyId = body.find(conn => conn.type === "spotify")?.id; - - await RestAPI.patch({ - url: Constants.Endpoints.CONNECTION("spotify", spotifyId), - body: { - show_activity: value, - }, - }); -} - function SpotifyActivityToggleButton() { - const { spotifyConnection } = settings.store; + const [loading, setLoading] = useState(true); + const [id, setId] = useState(null); + const [showActivity, setShowActivity] = useState(null); + + useEffect(() => { + (async () => { + const { body } = await RestAPI.get({ + url: Constants.Endpoints.CONNECTIONS, + }); + if (!body) return; + + const spotifyConn = body.find(c => c.type === "spotify"); + if (spotifyConn) { + setId(spotifyConn.id); + setShowActivity(spotifyConn.show_activity); + } + setLoading(false); + })(); + }, []); + + if (loading || !id || showActivity === null) return null; return (