Compare commits

..

No commits in common. "b23bbc958f06eff7415e6f524f96cc1f36a5f19a" and "36e623059d063894ca2fbcf275ef1623e9ec01d9" have entirely different histories.

19 changed files with 76 additions and 205 deletions

View file

@ -1,7 +1,7 @@
{ {
"name": "vencord", "name": "vencord",
"private": "true", "private": "true",
"version": "1.7.0", "version": "1.6.9",
"description": "The cutest Discord client mod", "description": "The cutest Discord client mod",
"homepage": "https://github.com/Vendicated/Vencord#readme", "homepage": "https://github.com/Vendicated/Vencord#readme",
"bugs": { "bugs": {

View file

@ -74,7 +74,7 @@ export interface MessageExtra {
} }
export type SendListener = (channelId: string, messageObj: MessageObject, extra: MessageExtra) => Promisable<void | { cancel: boolean; }>; export type SendListener = (channelId: string, messageObj: MessageObject, extra: MessageExtra) => Promisable<void | { cancel: boolean; }>;
export type EditListener = (channelId: string, messageId: string, messageObj: MessageObject) => Promisable<void | { cancel: boolean; }>; export type EditListener = (channelId: string, messageId: string, messageObj: MessageObject) => Promisable<void>;
const sendListeners = new Set<SendListener>(); const sendListeners = new Set<SendListener>();
const editListeners = new Set<EditListener>(); const editListeners = new Set<EditListener>();
@ -84,7 +84,7 @@ export async function _handlePreSend(channelId: string, messageObj: MessageObjec
for (const listener of sendListeners) { for (const listener of sendListeners) {
try { try {
const result = await listener(channelId, messageObj, extra); const result = await listener(channelId, messageObj, extra);
if (result?.cancel) { if (result && result.cancel === true) {
return true; return true;
} }
} catch (e) { } catch (e) {
@ -97,15 +97,11 @@ export async function _handlePreSend(channelId: string, messageObj: MessageObjec
export async function _handlePreEdit(channelId: string, messageId: string, messageObj: MessageObject) { export async function _handlePreEdit(channelId: string, messageId: string, messageObj: MessageObject) {
for (const listener of editListeners) { for (const listener of editListeners) {
try { try {
const result = await listener(channelId, messageId, messageObj); await listener(channelId, messageId, messageObj);
if (result?.cancel) {
return true;
}
} catch (e) { } catch (e) {
MessageEventsLogger.error("MessageEditHandler: Listener encountered an unknown error\n", e); MessageEventsLogger.error("MessageEditHandler: Listener encountered an unknown error\n", e);
} }
} }
return false;
} }
/** /**

View file

@ -25,13 +25,10 @@ export default definePlugin({
authors: [Devs.Arjix, Devs.hunt, Devs.Ven], authors: [Devs.Arjix, Devs.hunt, Devs.Ven],
patches: [ patches: [
{ {
find: ".Messages.EDIT_TEXTAREA_HELP", find: '"MessageActionCreators"',
replacement: { replacement: {
match: /(?<=,channel:\i\}\)\.then\().+?(?=return \i\.content!==this\.props\.message\.content&&\i\((.+?)\))/, match: /async editMessage\(.+?\)\{/,
replace: (match, args) => "" + replace: "$&await Vencord.Api.MessageEvents._handlePreEdit(...arguments);"
`async ${match}` +
`if(await Vencord.Api.MessageEvents._handlePreEdit(${args}))` +
"return Promise.resolve({shoudClear:true,shouldRefocus:true});"
} }
}, },
{ {

View file

@ -72,7 +72,7 @@ export default definePlugin({
replacement: [ replacement: [
// Add Decor avatar decoration hook to avatar decoration hook // Add Decor avatar decoration hook to avatar decoration hook
{ {
match: /(?<=TryItOut:\i,guildId:\i}\),)(?<=user:(\i).+?)/, match: /(?<=TryItOut:\i}\),)(?<=user:(\i).+?)/,
replace: "vcDecorAvatarDecoration=$self.useUserDecorAvatarDecoration($1)," replace: "vcDecorAvatarDecoration=$self.useUserDecorAvatarDecoration($1),"
}, },
// Use added hook // Use added hook

View file

@ -17,14 +17,14 @@
*/ */
import { addPreEditListener, addPreSendListener, removePreEditListener, removePreSendListener } from "@api/MessageEvents"; import { addPreEditListener, addPreSendListener, removePreEditListener, removePreSendListener } from "@api/MessageEvents";
import { definePluginSettings } from "@api/Settings"; import { definePluginSettings, Settings } from "@api/Settings";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { ApngBlendOp, ApngDisposeOp, importApngJs } from "@utils/dependencies"; import { ApngBlendOp, ApngDisposeOp, importApngJs } from "@utils/dependencies";
import { getCurrentGuild } from "@utils/discord"; import { getCurrentGuild } from "@utils/discord";
import { Logger } from "@utils/Logger"; import { Logger } from "@utils/Logger";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy, findStoreLazy, proxyLazyWebpack } from "@webpack"; import { findByPropsLazy, findStoreLazy, proxyLazyWebpack } from "@webpack";
import { Alerts, ChannelStore, EmojiStore, FluxDispatcher, Forms, lodash, Parser, PermissionsBits, PermissionStore, UploadHandler, UserSettingsActionCreators, UserStore } from "@webpack/common"; import { ChannelStore, EmojiStore, FluxDispatcher, lodash, Parser, PermissionStore, UploadHandler, UserSettingsActionCreators, UserStore } from "@webpack/common";
import type { Message } from "discord-types/general"; import type { Message } from "discord-types/general";
import { applyPalette, GIFEncoder, quantize } from "gifenc"; import { applyPalette, GIFEncoder, quantize } from "gifenc";
import type { ReactElement, ReactNode } from "react"; import type { ReactElement, ReactNode } from "react";
@ -51,6 +51,8 @@ const PreloadedUserSettingsActionCreators = proxyLazyWebpack(() => UserSettingsA
const AppearanceSettingsActionCreators = proxyLazyWebpack(() => searchProtoClassField("appearance", PreloadedUserSettingsActionCreators.ProtoClass)); const AppearanceSettingsActionCreators = proxyLazyWebpack(() => searchProtoClassField("appearance", PreloadedUserSettingsActionCreators.ProtoClass));
const ClientThemeSettingsActionsCreators = proxyLazyWebpack(() => searchProtoClassField("clientThemeSettings", AppearanceSettingsActionCreators)); const ClientThemeSettingsActionsCreators = proxyLazyWebpack(() => searchProtoClassField("clientThemeSettings", AppearanceSettingsActionCreators));
const USE_EXTERNAL_EMOJIS = 1n << 18n;
const USE_EXTERNAL_STICKERS = 1n << 37n;
const enum EmojiIntentions { const enum EmojiIntentions {
REACTION = 0, REACTION = 0,
@ -160,28 +162,8 @@ const settings = definePluginSettings({
description: "Whether to use hyperlinks when sending fake emojis and stickers", description: "Whether to use hyperlinks when sending fake emojis and stickers",
type: OptionType.BOOLEAN, type: OptionType.BOOLEAN,
default: true default: true
},
hyperLinkText: {
description: "What text the hyperlink should use. {{NAME}} will be replaced with the emoji name.",
type: OptionType.STRING,
default: "{{NAME}}"
} }
}).withPrivateSettings<{ });
disableEmbedPermissionCheck: boolean;
}>();
function hasPermission(channelId: string, permission: bigint) {
const channel = ChannelStore.getChannel(channelId);
if (!channel || channel.isPrivate()) return true;
return PermissionStore.can(permission, channel);
}
const hasExternalEmojiPerms = (channelId: string) => hasPermission(channelId, PermissionsBits.USE_EXTERNAL_EMOJIS);
const hasExternalStickerPerms = (channelId: string) => hasPermission(channelId, PermissionsBits.USE_EXTERNAL_STICKERS);
const hasEmbedPerms = (channelId: string) => hasPermission(channelId, PermissionsBits.EMBED_LINKS);
const hasAttachmentPerms = (channelId: string) => hasPermission(channelId, PermissionsBits.ATTACH_FILES);
export default definePlugin({ export default definePlugin({
name: "FakeNitro", name: "FakeNitro",
@ -714,6 +696,22 @@ export default definePlugin({
} }
}, },
hasPermissionToUseExternalEmojis(channelId: string): boolean {
const channel = ChannelStore.getChannel(channelId);
if (!channel || channel.isDM() || channel.isGroupDM() || channel.isMultiUserDM()) return true;
return PermissionStore.can(USE_EXTERNAL_EMOJIS, channel);
},
hasPermissionToUseExternalStickers(channelId: string) {
const channel = ChannelStore.getChannel(channelId);
if (!channel || channel.isDM() || channel.isGroupDM() || channel.isMultiUserDM()) return true;
return PermissionStore.can(USE_EXTERNAL_STICKERS, channel);
},
getStickerLink(stickerId: string) { getStickerLink(stickerId: string) {
return `https://media.discordapp.net/stickers/${stickerId}.png?size=${settings.store.stickerSize}`; return `https://media.discordapp.net/stickers/${stickerId}.png?size=${settings.store.stickerSize}`;
}, },
@ -724,7 +722,7 @@ export default definePlugin({
const { frames, width, height } = await parseURL(stickerLink); const { frames, width, height } = await parseURL(stickerLink);
const gif = GIFEncoder(); const gif = GIFEncoder();
const resolution = settings.store.stickerSize; const resolution = Settings.plugins.FakeNitro.stickerSize;
const canvas = document.createElement("canvas"); const canvas = document.createElement("canvas");
canvas.width = resolution; canvas.width = resolution;
@ -785,38 +783,9 @@ export default definePlugin({
return (!origStr[offset] || /\s/.test(origStr[offset])) ? "" : " "; return (!origStr[offset] || /\s/.test(origStr[offset])) ? "" : " ";
} }
function cannotEmbedNotice() { this.preSend = addPreSendListener((channelId, messageObj, extra) => {
return new Promise<boolean>(resolve => {
Alerts.show({
title: "Hold on!",
body: <div>
<Forms.FormText>
You are trying to send/edit a message that contains a FakeNitro emoji or sticker
, however you do not have permissions to embed links in the current channel.
Are you sure you want to send this message? Your FakeNitro items will appear as a link only.
</Forms.FormText>
<Forms.FormText type={Forms.FormText.Types.DESCRIPTION}>
You can disable this notice in the plugin settings.
</Forms.FormText>
</div>,
confirmText: "Send Anyway",
cancelText: "Cancel",
secondaryConfirmText: "Do not show again",
onConfirm: () => resolve(true),
onCloseCallback: () => setImmediate(() => resolve(false)),
onConfirmSecondary() {
settings.store.disableEmbedPermissionCheck = true;
resolve(true);
}
});
});
}
this.preSend = addPreSendListener(async (channelId, messageObj, extra) => {
const { guildId } = this; const { guildId } = this;
let hasBypass = false;
stickerBypass: { stickerBypass: {
if (!s.enableStickerBypass) if (!s.enableStickerBypass)
break stickerBypass; break stickerBypass;
@ -829,7 +798,7 @@ export default definePlugin({
if ("pack_id" in sticker) if ("pack_id" in sticker)
break stickerBypass; break stickerBypass;
const canUseStickers = this.canUseStickers && hasExternalStickerPerms(channelId); const canUseStickers = this.canUseStickers && this.hasPermissionToUseExternalStickers(channelId);
if (sticker.available !== false && (canUseStickers || sticker.guild_id === guildId)) if (sticker.available !== false && (canUseStickers || sticker.guild_id === guildId))
break stickerBypass; break stickerBypass;
@ -843,24 +812,9 @@ export default definePlugin({
} }
if (sticker.format_type === StickerType.APNG) { if (sticker.format_type === StickerType.APNG) {
if (!hasAttachmentPerms(channelId)) { this.sendAnimatedSticker(link, sticker.id, channelId);
Alerts.show({
title: "Hold on!",
body: <div>
<Forms.FormText>
You cannot send this message because it contains an animated FakeNitro sticker,
and you do not have permissions to attach files in the current channel. Please remove the sticker to proceed.
</Forms.FormText>
</div>
});
} else {
this.sendAnimatedSticker(link, sticker.id, channelId);
}
return { cancel: true }; return { cancel: true };
} else { } else {
hasBypass = true;
const url = new URL(link); const url = new URL(link);
url.searchParams.set("name", sticker.name); url.searchParams.set("name", sticker.name);
@ -870,47 +824,35 @@ export default definePlugin({
} }
if (s.enableEmojiBypass) { if (s.enableEmojiBypass) {
const canUseEmotes = this.canUseEmotes && hasExternalEmojiPerms(channelId); const canUseEmotes = this.canUseEmotes && this.hasPermissionToUseExternalEmojis(channelId);
for (const emoji of messageObj.validNonShortcutEmojis) { for (const emoji of messageObj.validNonShortcutEmojis) {
if (!emoji.require_colons) continue; if (!emoji.require_colons) continue;
if (emoji.available !== false && canUseEmotes) continue; if (emoji.available !== false && canUseEmotes) continue;
if (emoji.guildId === guildId && !emoji.animated) continue; if (emoji.guildId === guildId && !emoji.animated) continue;
hasBypass = true;
const emojiString = `<${emoji.animated ? "a" : ""}:${emoji.originalName || emoji.name}:${emoji.id}>`; const emojiString = `<${emoji.animated ? "a" : ""}:${emoji.originalName || emoji.name}:${emoji.id}>`;
const url = new URL(emoji.url); const url = new URL(emoji.url);
url.searchParams.set("size", s.emojiSize.toString()); url.searchParams.set("size", s.emojiSize.toString());
url.searchParams.set("name", emoji.name); url.searchParams.set("name", emoji.name);
const linkText = s.hyperLinkText.replaceAll("{{NAME}}", emoji.name);
messageObj.content = messageObj.content.replace(emojiString, (match, offset, origStr) => { messageObj.content = messageObj.content.replace(emojiString, (match, offset, origStr) => {
return `${getWordBoundary(origStr, offset - 1)}${s.useHyperLinks ? `[${linkText}](${url})` : url}${getWordBoundary(origStr, offset + match.length)}`; return `${getWordBoundary(origStr, offset - 1)}${s.useHyperLinks ? `[${emoji.name}](${url})` : url}${getWordBoundary(origStr, offset + match.length)}`;
}); });
} }
} }
if (hasBypass && !s.disableEmbedPermissionCheck && !hasEmbedPerms(channelId)) {
if (!await cannotEmbedNotice()) {
return { cancel: true };
}
}
return { cancel: false }; return { cancel: false };
}); });
this.preEdit = addPreEditListener(async (channelId, __, messageObj) => { this.preEdit = addPreEditListener((channelId, __, messageObj) => {
if (!s.enableEmojiBypass) return; if (!s.enableEmojiBypass) return;
const canUseEmotes = this.canUseEmotes && this.hasPermissionToUseExternalEmojis(channelId);
const { guildId } = this; const { guildId } = this;
let hasBypass = false;
const canUseEmotes = this.canUseEmotes && hasExternalEmojiPerms(channelId);
messageObj.content = messageObj.content.replace(/(?<!\\)<a?:(?:\w+):(\d+)>/ig, (emojiStr, emojiId, offset, origStr) => { messageObj.content = messageObj.content.replace(/(?<!\\)<a?:(?:\w+):(\d+)>/ig, (emojiStr, emojiId, offset, origStr) => {
const emoji = EmojiStore.getCustomEmojiById(emojiId); const emoji = EmojiStore.getCustomEmojiById(emojiId);
if (emoji == null) return emojiStr; if (emoji == null) return emojiStr;
@ -918,24 +860,12 @@ export default definePlugin({
if (emoji.available !== false && canUseEmotes) return emojiStr; if (emoji.available !== false && canUseEmotes) return emojiStr;
if (emoji.guildId === guildId && !emoji.animated) return emojiStr; if (emoji.guildId === guildId && !emoji.animated) return emojiStr;
hasBypass = true;
const url = new URL(emoji.url); const url = new URL(emoji.url);
url.searchParams.set("size", s.emojiSize.toString()); url.searchParams.set("size", s.emojiSize.toString());
url.searchParams.set("name", emoji.name); url.searchParams.set("name", emoji.name);
const linkText = s.hyperLinkText.replaceAll("{{NAME}}", emoji.name); return `${getWordBoundary(origStr, offset - 1)}${s.useHyperLinks ? `[${emoji.name}](${url})` : url}${getWordBoundary(origStr, offset + emojiStr.length)}`;
return `${getWordBoundary(origStr, offset - 1)}${s.useHyperLinks ? `[${linkText}](${url})` : url}${getWordBoundary(origStr, offset + emojiStr.length)}`;
}); });
if (hasBypass && !s.disableEmbedPermissionCheck && !hasEmbedPerms(channelId)) {
if (!await cannotEmbedNotice()) {
return { cancel: true };
}
}
return { cancel: false };
}); });
}, },

View file

@ -9,9 +9,6 @@
box-shadow: inset 0 0 10px 2px grey; box-shadow: inset 0 0 10px 2px grey;
filter: drop-shadow(0 0 2px grey); filter: drop-shadow(0 0 2px grey);
pointer-events: none; pointer-events: none;
/* negate the border offsetting the lens */
margin: -2px;
} }
.vc-imgzoom-square { .vc-imgzoom-square {

View file

@ -170,11 +170,6 @@ const settings = definePluginSettings({
} }
], ],
}, },
showLastFmLogo: {
description: "show the Last.fm logo by the album cover",
type: OptionType.BOOLEAN,
default: true,
}
}); });
export default definePlugin({ export default definePlugin({
@ -281,10 +276,8 @@ export default definePlugin({
{ {
large_image: await getApplicationAsset(largeImage), large_image: await getApplicationAsset(largeImage),
large_text: trackData.album || undefined, large_text: trackData.album || undefined,
...(settings.store.showLastFmLogo && { small_image: await getApplicationAsset("lastfm-small"),
small_image: await getApplicationAsset("lastfm-small"), small_text: "Last.fm",
small_text: "Last.fm"
}),
} : { } : {
large_image: await getApplicationAsset("lastfm-large"), large_image: await getApplicationAsset("lastfm-large"),
large_text: trackData.album || undefined, large_text: trackData.album || undefined,

View file

@ -16,18 +16,16 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { definePluginSettings,migratePluginSettings } from "@api/Settings"; import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy } from "@webpack"; import { findByPropsLazy } from "@webpack";
const { updateGuildNotificationSettings } = findByPropsLazy("updateGuildNotificationSettings"); const { updateGuildNotificationSettings } = findByPropsLazy("updateGuildNotificationSettings");
const { toggleShowAllChannels } = findByPropsLazy("toggleShowAllChannels");
const { isOptInEnabledForGuild } = findByPropsLazy("isOptInEnabledForGuild");
const settings = definePluginSettings({ const settings = definePluginSettings({
guild: { guild: {
description: "Mute Guild automatically", description: "Mute Guild",
type: OptionType.BOOLEAN, type: OptionType.BOOLEAN,
default: true default: true
}, },
@ -40,20 +38,13 @@ const settings = definePluginSettings({
description: "Suppress All Role @mentions", description: "Suppress All Role @mentions",
type: OptionType.BOOLEAN, type: OptionType.BOOLEAN,
default: true default: true
},
showAllChannels: {
description: "Show all channels automatically",
type: OptionType.BOOLEAN,
default: true
} }
}); });
migratePluginSettings("NewGuildSettings", "MuteNewGuild");
export default definePlugin({ export default definePlugin({
name: "NewGuildSettings", name: "MuteNewGuild",
description: "Automatically mute new servers and change various other settings upon joining", description: "Mutes newly joined guilds",
tags: ["MuteNewGuild", "mute", "server"], authors: [Devs.Glitch, Devs.Nuckyz, Devs.carince],
authors: [Devs.Glitch, Devs.Nuckyz, Devs.carince, Devs.Mopi],
patches: [ patches: [
{ {
find: ",acceptInvite(", find: ",acceptInvite(",
@ -79,9 +70,7 @@ export default definePlugin({
muted: settings.store.guild, muted: settings.store.guild,
suppress_everyone: settings.store.everyone, suppress_everyone: settings.store.everyone,
suppress_roles: settings.store.role suppress_roles: settings.store.role
}); }
if (settings.store.showAllChannels && isOptInEnabledForGuild(guildId)) { );
toggleShowAllChannels(guildId);
}
} }
}); });

View file

@ -59,7 +59,7 @@ export function authorize(callback?: any) {
const url = new URL(response.location); const url = new URL(response.location);
url.searchParams.append("clientMod", "vencord"); url.searchParams.append("clientMod", "vencord");
const res = await fetch(url, { const res = await fetch(url, {
headers: { Accept: "application/json" } headers: new Headers({ Accept: "application/json" })
}); });
if (!res.ok) { if (!res.ok) {

View file

@ -32,7 +32,6 @@ const { Editor, Transforms } = findByPropsLazy("Editor", "Transforms");
const { ChatInputTypes } = findByPropsLazy("ChatInputTypes"); const { ChatInputTypes } = findByPropsLazy("ChatInputTypes");
const InputComponent = LazyComponent(() => find(m => m.default?.type?.render?.toString().includes("default.CHANNEL_TEXT_AREA")).default); const InputComponent = LazyComponent(() => find(m => m.default?.type?.render?.toString().includes("default.CHANNEL_TEXT_AREA")).default);
const { createChannelRecordFromServer } = findByPropsLazy("createChannelRecordFromServer");
interface UserProps { interface UserProps {
discordId: string; discordId: string;
@ -126,7 +125,19 @@ export function ReviewsInputComponent({ discordId, isAuthor, refetch, name }: {
const inputType = ChatInputTypes.FORM; const inputType = ChatInputTypes.FORM;
inputType.disableAutoFocus = true; inputType.disableAutoFocus = true;
const channel = createChannelRecordFromServer({ id: "0", type: 1 }); const channel = {
flags_: 256,
guild_id_: null,
id: "0",
getGuildId: () => null,
isPrivate: () => true,
isActiveThread: () => false,
isArchivedLockedThread: () => false,
isDM: () => true,
roles: { "0": { permissions: 0n } },
getRecipientId: () => "0",
hasFlag: () => false,
};
return ( return (
<> <>

View file

@ -118,10 +118,10 @@ export async function addReview(review: any): Promise<Response | null> {
export async function deleteReview(id: number): Promise<Response | null> { export async function deleteReview(id: number): Promise<Response | null> {
return await rdbRequest(`/users/${id}/reviews`, { return await rdbRequest(`/users/${id}/reviews`, {
method: "DELETE", method: "DELETE",
headers: { headers: new Headers({
"Content-Type": "application/json", "Content-Type": "application/json",
Accept: "application/json", Accept: "application/json",
}, }),
body: JSON.stringify({ body: JSON.stringify({
reviewid: id reviewid: id
}) })
@ -135,10 +135,10 @@ export async function deleteReview(id: number): Promise<Response | null> {
export async function reportReview(id: number) { export async function reportReview(id: number) {
const res = await rdbRequest("/reports", { const res = await rdbRequest("/reports", {
method: "PUT", method: "PUT",
headers: { headers: new Headers({
"Content-Type": "application/json", "Content-Type": "application/json",
Accept: "application/json", Accept: "application/json",
}, }),
body: JSON.stringify({ body: JSON.stringify({
reviewid: id, reviewid: id,
}) })
@ -150,10 +150,10 @@ export async function reportReview(id: number) {
async function patchBlock(action: "block" | "unblock", userId: string) { async function patchBlock(action: "block" | "unblock", userId: string) {
const res = await rdbRequest("/blocks", { const res = await rdbRequest("/blocks", {
method: "PATCH", method: "PATCH",
headers: { headers: new Headers({
"Content-Type": "application/json", "Content-Type": "application/json",
Accept: "application/json", Accept: "application/json",
}, }),
body: JSON.stringify({ body: JSON.stringify({
action: action, action: action,
discordId: userId discordId: userId
@ -180,9 +180,9 @@ export const unblockUser = (userId: string) => patchBlock("unblock", userId);
export async function fetchBlocks(): Promise<ReviewDBUser[]> { export async function fetchBlocks(): Promise<ReviewDBUser[]> {
const res = await rdbRequest("/blocks", { const res = await rdbRequest("/blocks", {
method: "GET", method: "GET",
headers: { headers: new Headers({
Accept: "application/json", Accept: "application/json",
} })
}); });
if (!res.ok) throw new Error(`${res.status}: ${res.statusText}`); if (!res.ok) throw new Error(`${res.status}: ${res.statusText}`);

View file

@ -371,10 +371,6 @@ export function Player() {
if (!track || !device?.is_active || shouldHide) if (!track || !device?.is_active || shouldHide)
return null; return null;
const exportTrackImageStyle = {
"--vc-spotify-track-image": `url(${track?.album?.image?.url || ""})`,
} as React.CSSProperties;
return ( return (
<ErrorBoundary fallback={() => ( <ErrorBoundary fallback={() => (
<div className="vc-spotify-fallback"> <div className="vc-spotify-fallback">
@ -382,7 +378,7 @@ export function Player() {
<p >Check the console for errors</p> <p >Check the console for errors</p>
</div> </div>
)}> )}>
<div id={cl("player")} style={exportTrackImageStyle}> <div id={cl("player")}>
<Info track={track} /> <Info track={track} />
<SeekBar /> <SeekBar />
<Controls /> <Controls />

View file

@ -31,7 +31,7 @@ function toggleHoverControls(value: boolean) {
export default definePlugin({ export default definePlugin({
name: "SpotifyControls", name: "SpotifyControls",
description: "Adds a Spotify player above the account panel", description: "Adds a Spotify player above the account panel",
authors: [Devs.Ven, Devs.afn, Devs.KraXen72, Devs.Av32000], authors: [Devs.Ven, Devs.afn, Devs.KraXen72],
options: { options: {
hoverControls: { hoverControls: {
description: "Show controls on hover", description: "Show controls on hover",

View file

@ -170,16 +170,9 @@
/* these importants are necessary, it applies a width and height through inline styles */ /* these importants are necessary, it applies a width and height through inline styles */
height: 10px !important; height: 10px !important;
width: 10px !important; width: 10px !important;
margin-top: 4px;
background-color: var(--interactive-normal); background-color: var(--interactive-normal);
border-color: var(--interactive-normal); border-color: var(--interactive-normal);
color: var(--interactive-normal); color: var(--interactive-normal);
opacity: 0;
transition: opacity 0.1s;
}
#vc-spotify-progress-bar:hover > [class^="slider"] [class^="grabber"] {
opacity: 1;
} }
#vc-spotify-progress-text { #vc-spotify-progress-text {

View file

@ -7,7 +7,6 @@
import { definePluginSettings } from "@api/Settings"; import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { UserStore } from "@webpack/common";
export const settings = definePluginSettings({ export const settings = definePluginSettings({
superReactByDefault: { superReactByDefault: {
@ -50,7 +49,7 @@ export default definePlugin({
find: ".trackEmojiSearchEmpty,200", find: ".trackEmojiSearchEmpty,200",
replacement: { replacement: {
match: /(\.trackEmojiSearchEmpty,200(?=.+?isBurstReaction:(\i).+?(\i===\i\.EmojiIntention.REACTION)).+?\[\2,\i\]=\i\.useState\().+?\)/, match: /(\.trackEmojiSearchEmpty,200(?=.+?isBurstReaction:(\i).+?(\i===\i\.EmojiIntention.REACTION)).+?\[\2,\i\]=\i\.useState\().+?\)/,
replace: (_, rest, isBurstReactionVariable, isReactionIntention) => `${rest}$self.shouldSuperReactByDefault&&${isReactionIntention})` replace: (_, rest, isBurstReactionVariable, isReactionIntention) => `${rest}$self.settings.store.superReactByDefault&&${isReactionIntention})`
} }
} }
], ],
@ -60,9 +59,5 @@ export default definePlugin({
if (settings.store.unlimitedSuperReactionPlaying) return true; if (settings.store.unlimitedSuperReactionPlaying) return true;
if (playingCount <= settings.store.superReactionPlayingLimit) return true; if (playingCount <= settings.store.superReactionPlayingLimit) return true;
return false; return false;
},
get shouldSuperReactByDefault() {
return settings.store.superReactByDefault && UserStore.getCurrentUser().premiumType != null;
} }
}); });

View file

@ -20,15 +20,13 @@ import "./styles.css";
import { addContextMenuPatch, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu"; import { addContextMenuPatch, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu";
import { Microphone } from "@components/Icons"; import { Microphone } from "@components/Icons";
import { Link } from "@components/Link";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { Margins } from "@utils/margins";
import { ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, openModal } from "@utils/modal"; import { ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, openModal } from "@utils/modal";
import { useAwaiter } from "@utils/react"; import { useAwaiter } from "@utils/react";
import definePlugin from "@utils/types"; import definePlugin from "@utils/types";
import { chooseFile } from "@utils/web"; import { chooseFile } from "@utils/web";
import { findByPropsLazy, findStoreLazy } from "@webpack"; import { findByPropsLazy, findStoreLazy } from "@webpack";
import { Button, Card, FluxDispatcher, Forms, lodash, Menu, MessageActions, PermissionsBits, PermissionStore, RestAPI, SelectedChannelStore, showToast, SnowflakeUtils, Toasts, useEffect, useState } from "@webpack/common"; import { Button, FluxDispatcher, Forms, lodash, Menu, MessageActions, PermissionsBits, PermissionStore, RestAPI, SelectedChannelStore, showToast, SnowflakeUtils, Toasts, useEffect, useState } from "@webpack/common";
import { ComponentType } from "react"; import { ComponentType } from "react";
import { VoiceRecorderDesktop } from "./DesktopRecorder"; import { VoiceRecorderDesktop } from "./DesktopRecorder";
@ -166,11 +164,6 @@ function Modal({ modalProps }: { modalProps: ModalProps; }) {
fallbackValue: EMPTY_META, fallbackValue: EMPTY_META,
}); });
const isUnsupportedFormat = blob && (
!blob.type.startsWith("audio/ogg")
|| blob.type.includes("codecs") && !blob.type.includes("opus")
);
return ( return (
<ModalRoot {...modalProps}> <ModalRoot {...modalProps}>
<ModalHeader> <ModalHeader>
@ -207,16 +200,6 @@ function Modal({ modalProps }: { modalProps: ModalProps; }) {
recording={isRecording} recording={isRecording}
/> />
{isUnsupportedFormat && (
<Card className={`vc-plugins-restart-card ${Margins.top16}`}>
<Forms.FormText>Voice Messages have to be OggOpus to be playable on iOS. This file is <code>{blob.type}</code> so it will not be playable on iOS.</Forms.FormText>
<Forms.FormText className={Margins.top8}>
To fix it, first convert it to OggOpus, for example using the <Link href="https://convertio.co/mp3-opus/">convertio web converter</Link>
</Forms.FormText>
</Card>
)}
</ModalContent> </ModalContent>
<ModalFooter> <ModalFooter>

View file

@ -69,14 +69,14 @@ function getReactionsWithQueue(msg: Message, e: ReactionEmoji, type: number) {
function makeRenderMoreUsers(users: User[]) { function makeRenderMoreUsers(users: User[]) {
return function renderMoreUsers(_label: string, _count: number) { return function renderMoreUsers(_label: string, _count: number) {
return ( return (
<Tooltip text={users.slice(4).map(u => u.username).join(", ")} > <Tooltip text={users.slice(5).map(u => u.username).join(", ")} >
{({ onMouseEnter, onMouseLeave }) => ( {({ onMouseEnter, onMouseLeave }) => (
<div <div
className={AvatarStyles.moreUsers} className={AvatarStyles.moreUsers}
onMouseEnter={onMouseEnter} onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave} onMouseLeave={onMouseLeave}
> >
+{users.length - 4} +{users.length - 5}
</div> </div>
)} )}
</Tooltip > </Tooltip >

View file

@ -399,10 +399,6 @@ export const Devs = /* #__PURE__*/ Object.freeze({
name: "maisy", name: "maisy",
id: 257109471589957632n, id: 257109471589957632n,
}, },
Mopi: {
name: "Mopi",
id: 1022189106614243350n
},
Grzesiek11: { Grzesiek11: {
name: "Grzesiek11", name: "Grzesiek11",
id: 368475654662127616n, id: 368475654662127616n,
@ -414,10 +410,6 @@ export const Devs = /* #__PURE__*/ Object.freeze({
coolelectronics: { coolelectronics: {
name: "coolelectronics", name: "coolelectronics",
id: 696392247205298207n, id: 696392247205298207n,
},
Av32000: {
name: "Av32000",
id: 593436735380127770n,
} }
} satisfies Record<string, Dev>); } satisfies Record<string, Dev>);

View file

@ -59,7 +59,6 @@ export interface Alerts {
onCancel?(): void; onCancel?(): void;
onConfirm?(): void; onConfirm?(): void;
onConfirmSecondary?(): void; onConfirmSecondary?(): void;
onCloseCallback?(): void;
}): void; }): void;
/** This is a noop, it does nothing. */ /** This is a noop, it does nothing. */
close(): void; close(): void;