From 62277770a8e5eec921fb407863c8c1d1d8e466b5 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Thu, 28 Sep 2023 02:42:55 +0200 Subject: [PATCH 01/38] bump to v1.5.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d9563914..3fcac350 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vencord", "private": "true", - "version": "1.5.2", + "version": "1.5.3", "description": "The cutest Discord client mod", "homepage": "https://github.com/Vendicated/Vencord#readme", "bugs": { From 88b06191b926de40dc9f5abfbe83f7a26ab0dac4 Mon Sep 17 00:00:00 2001 From: AutumnVN Date: Fri, 29 Sep 2023 05:46:33 +0700 Subject: [PATCH 02/38] fix modal image + reviewdb bot tag (#1761) * fix modal image * fix reviewdb bot tag --- src/plugins/reviewDB/components/ReviewComponent.tsx | 2 +- src/webpack/common/classes.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/reviewDB/components/ReviewComponent.tsx b/src/plugins/reviewDB/components/ReviewComponent.tsx index 1278a508..9e7df369 100644 --- a/src/plugins/reviewDB/components/ReviewComponent.tsx +++ b/src/plugins/reviewDB/components/ReviewComponent.tsx @@ -43,7 +43,7 @@ export default LazyComponent(() => { p("container", "isHeader"), p("avatar", "zalgo"), p("button", "wrapper", "selected"), - p("botTag") + p("botTag", "botTagRegular") ); const dateFormat = new Intl.DateTimeFormat(); diff --git a/src/webpack/common/classes.ts b/src/webpack/common/classes.ts index 5c1a6763..8bc76409 100644 --- a/src/webpack/common/classes.ts +++ b/src/webpack/common/classes.ts @@ -20,5 +20,5 @@ import { findByPropsLazy } from "@webpack"; import * as t from "./types/classes"; -export const ModalImageClasses: t.ImageModalClasses = findByPropsLazy("image", "modal"); +export const ModalImageClasses: t.ImageModalClasses = findByPropsLazy("image", "modal", "responsiveWidthMobile"); export const ButtonWrapperClasses: t.ButtonWrapperClasses = findByPropsLazy("buttonWrapper", "buttonContent"); From c8b77bb187e9f5d9ba6e255aa5d473a3d1de21ff Mon Sep 17 00:00:00 2001 From: V Date: Fri, 29 Sep 2023 19:00:50 +0200 Subject: [PATCH 03/38] Update bug_report.yml --- .github/ISSUE_TEMPLATE/bug_report.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index e7afec3c..74b2a418 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -4,6 +4,18 @@ labels: [bug] title: "[Bug] " body: + - type: markdown + attributes: + value: | + # READ THIS BEFORE OPENING AN ISSUE + + This form is ONLY FOR DEVELOPERS. YOUR ISSUE WILL BE CLOSED AND YOU WILL POSSIBLY BE BLOCKED FROM THE REPOSITORY IF YOU IGNORE THIS. + + DO NOT USE THIS FORM, unless + - you are a vencord contributor + - you were given explicit permission to use this form by a moderator in our support server + - you are filing a security related report + - type: input id: discord attributes: @@ -64,3 +76,5 @@ body: options: - label: I am using Discord Stable or tried on Stable and this bug happens there as well required: true + - label: I have read the requirements for opening an issue above + required: true From 8a026060c760fd33751b7ba5c2bbff62818eddcd Mon Sep 17 00:00:00 2001 From: V <vendicated@riseup.net> Date: Fri, 29 Sep 2023 19:01:55 +0200 Subject: [PATCH 04/38] Update blank.yml --- .github/ISSUE_TEMPLATE/blank.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/blank.yml b/.github/ISSUE_TEMPLATE/blank.yml index ba2b15b0..e8ca246d 100644 --- a/.github/ISSUE_TEMPLATE/blank.yml +++ b/.github/ISSUE_TEMPLATE/blank.yml @@ -2,9 +2,29 @@ name: Blank Issue description: Create a blank issue. ALWAYS FIRST USE OUR SUPPORT CHANNEL! ONLY USE THIS FORM IF YOU ARE A CONTRIBUTOR OR WERE TOLD TO DO SO IN THE SUPPORT CHANNEL. body: + - type: markdown + attributes: + value: | + # READ THIS BEFORE OPENING AN ISSUE + + This form is ONLY FOR DEVELOPERS. YOUR ISSUE WILL BE CLOSED AND YOU WILL POSSIBLY BE BLOCKED FROM THE REPOSITORY IF YOU IGNORE THIS. + + DO NOT USE THIS FORM, unless + - you are a vencord contributor + - you were given explicit permission to use this form by a moderator in our support server + - you are filing a security related report + - type: textarea id: content attributes: label: Content validations: required: true + + - type: checkboxes + id: agreement-check + attributes: + label: Request Agreement + options: + - label: I have read the requirements for opening an issue above + required: true From abf8667a5dd9a4850f84b4918a0f0fa5dbc42b3b Mon Sep 17 00:00:00 2001 From: whqwert <94757998+whqwert@users.noreply.github.com> Date: Sun, 1 Oct 2023 21:19:31 -0500 Subject: [PATCH 05/38] =?UTF-8?q?fix(plugin):=20Party=20mode=20?= =?UTF-8?q?=F0=9F=8E=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/partyMode/index.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/plugins/partyMode/index.ts b/src/plugins/partyMode/index.ts index bb822f5a..06e87195 100644 --- a/src/plugins/partyMode/index.ts +++ b/src/plugins/partyMode/index.ts @@ -19,10 +19,7 @@ import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { findStoreLazy } from "@webpack"; -import { GenericStore } from "@webpack/common"; - -const PoggerModeSettingsStore: GenericStore = findStoreLazy("PoggermodeSettingsStore"); +import { FluxDispatcher } from "@webpack/common"; const enum Intensity { Normal, @@ -61,9 +58,12 @@ export default definePlugin({ }); function setPoggerState(state: boolean) { - Object.assign(PoggerModeSettingsStore.__getLocalVars().state, { - enabled: state, - settingsVisible: state + FluxDispatcher.dispatch({ + type: "POGGERMODE_SETTINGS_UPDATE", + settings: { + enabled: state, + settingsVisible: state + } }); } @@ -101,5 +101,8 @@ function setSettings(intensity: Intensity) { } } - Object.assign(PoggerModeSettingsStore.__getLocalVars().state, state); + FluxDispatcher.dispatch({ + type: "POGGERMODE_SETTINGS_UPDATE", + settings: state + }); } From 8dd5eeead2f4f48ec7dfa3dd812475e97b322146 Mon Sep 17 00:00:00 2001 From: Lewis Crichton <lewi@lewisakura.moe> Date: Tue, 3 Oct 2023 00:26:57 +0100 Subject: [PATCH 06/38] feat(plugin): PermissionFreeWill (#1763) Co-authored-by: V <vendicated@riseup.net> --- src/plugins/permissionFreeWill/README.md | 9 ++++ src/plugins/permissionFreeWill/index.ts | 56 ++++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 src/plugins/permissionFreeWill/README.md create mode 100644 src/plugins/permissionFreeWill/index.ts diff --git a/src/plugins/permissionFreeWill/README.md b/src/plugins/permissionFreeWill/README.md new file mode 100644 index 00000000..ca30575f --- /dev/null +++ b/src/plugins/permissionFreeWill/README.md @@ -0,0 +1,9 @@ +# PermissionFreeWill + +Removes the client-side restrictions that prevent editing channel permissions, such as permission lockouts ("Pretty sure +you don't want to do this") and onboarding requirements ("Making this change will make your server incompatible [...]") + +## Warning + +This plugin will let you create permissions in servers that **WILL** lock you out of channels until an administrator +can resolve it for you. Please be careful with the overwrites you are making and check carefully. diff --git a/src/plugins/permissionFreeWill/index.ts b/src/plugins/permissionFreeWill/index.ts new file mode 100644 index 00000000..f3fdf159 --- /dev/null +++ b/src/plugins/permissionFreeWill/index.ts @@ -0,0 +1,56 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2023 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { definePluginSettings } from "@api/Settings"; +import { Devs } from "@utils/constants"; +import definePlugin, { OptionType } from "@utils/types"; + +const settings = definePluginSettings({ + lockout: { + type: OptionType.BOOLEAN, + default: true, + description: 'Bypass the permission lockout prevention ("Pretty sure you don\'t want to do this")', + restartNeeded: true + }, + onboarding: { + type: OptionType.BOOLEAN, + default: true, + description: 'Bypass the onboarding requirements ("Making this change will make your server incompatible [...]")', + restartNeeded: true + } +}); + +export default definePlugin({ + name: "PermissionFreeWill", + description: "Disables the client-side restrictions for channel permission management.", + authors: [Devs.lewisakura], + + patches: [ + // Permission lockout, just set the check to true + { + find: "Messages.SELF_DENY_PERMISSION_BODY", + replacement: [ + { + match: /case"DENY":.{0,50}if\((?=\i\.\i\.can)/, + replace: "$&true||" + } + ], + predicate: () => settings.store.lockout + }, + // Onboarding, same thing but we need to prevent the check + { + find: "Messages.ONBOARDING_CHANNEL_THRESHOLD_WARNING", + replacement: [ + { + match: /case 1:if\((?=!\i\.sent.{20,30}Messages\.CANNOT_CHANGE_CHANNEL_PERMS)/, + replace: "$&false&&" + } + ], + predicate: () => settings.store.onboarding + } + ], + settings +}); From 9891791fa76d25db5f312622b1a7d34a6c2734b8 Mon Sep 17 00:00:00 2001 From: Dea <dea-banana@riseup.net> Date: Mon, 2 Oct 2023 23:53:14 +0000 Subject: [PATCH 07/38] feat(plugin): onePingPerDM (#1757) Co-authored-by: V <vendicated@riseup.net> --- src/plugins/onePingPerDM/README.md | 7 ++++++ src/plugins/onePingPerDM/index.ts | 39 ++++++++++++++++++++++++++++++ src/utils/constants.ts | 6 ++++- 3 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 src/plugins/onePingPerDM/README.md create mode 100644 src/plugins/onePingPerDM/index.ts diff --git a/src/plugins/onePingPerDM/README.md b/src/plugins/onePingPerDM/README.md new file mode 100644 index 00000000..43f89b72 --- /dev/null +++ b/src/plugins/onePingPerDM/README.md @@ -0,0 +1,7 @@ +# OnePingPerDM +If unread messages are sent by a user in DMs multiple times, you'll only receive one audio ping. Read the messages to reset the limit + +## Purpose +- Prevents ping audio spam in DMs +- Be able to distinguish more than one ping as multiple users +- Be less annoyed while gaming diff --git a/src/plugins/onePingPerDM/index.ts b/src/plugins/onePingPerDM/index.ts new file mode 100644 index 00000000..47502ebe --- /dev/null +++ b/src/plugins/onePingPerDM/index.ts @@ -0,0 +1,39 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2023 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { Devs } from "@utils/constants"; +import definePlugin from "@utils/types"; +import { ChannelStore, ReadStateStore } from "@webpack/common"; +import { Message } from "discord-types/general"; + +const enum ChannelType { + DM = 1, + GROUP_DM = 3 +} + +export default definePlugin({ + name: "OnePingPerDM", + description: "If unread messages are sent by a user in DMs multiple times, you'll only receive one audio ping. Read the messages to reset the limit", + authors: [Devs.ProffDea], + patches: [{ + find: ".getDesktopType()===", + replacement: [{ + match: /if\((\i\.\i\.getDesktopType\(\)===\i\.\i\.NEVER)\){/, + replace: "if($1){if(!$self.isPrivateChannelRead(arguments[0]?.message))return;" + }, + { + match: /sound:(\i\?\i:void 0,volume:\i,onClick:)/, + replace: "sound:!$self.isPrivateChannelRead(arguments[0]?.message)?undefined:$1" + }] + }], + isPrivateChannelRead(message: Message) { + const channelType = ChannelStore.getChannel(message.channel_id)?.type; + if (channelType !== ChannelType.DM && channelType !== ChannelType.GROUP_DM) { + return false; + } + return ReadStateStore.getOldestUnreadMessageId(message.channel_id) === message.id; + }, +}); diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 7264c40d..e80298d1 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -374,7 +374,11 @@ export const Devs = /* #__PURE__*/ Object.freeze({ archeruwu: { name: "archer_uwu", id: 160068695383736320n - } + }, + ProffDea: { + name: "ProffDea", + id: 609329952180928513n + }, } satisfies Record<string, Dev>); // iife so #__PURE__ works correctly From 6db9721c067bac510ea480067029f8c3c0d09d98 Mon Sep 17 00:00:00 2001 From: Vendicated <vendicated@riseup.net> Date: Tue, 3 Oct 2023 02:17:28 +0200 Subject: [PATCH 08/38] ReviewDB: fix usericons appearing over modals --- src/plugins/reviewDB/components/ReviewComponent.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/reviewDB/components/ReviewComponent.tsx b/src/plugins/reviewDB/components/ReviewComponent.tsx index 9e7df369..07bcdb28 100644 --- a/src/plugins/reviewDB/components/ReviewComponent.tsx +++ b/src/plugins/reviewDB/components/ReviewComponent.tsx @@ -94,7 +94,7 @@ export default LazyComponent(() => { className={classes(avatar, clickable)} onClick={openModal} src={review.sender.profilePhoto || "/assets/1f0bfc0865d324c2587920a7d80c609b.png?size=128"} - style={{ left: "0px" }} + style={{ left: "0px", zIndex: 0 }} /> <div style={{ display: "inline-flex", justifyContent: "center", alignItems: "center" }}> <span From 1f38a8eeab3b2b184a779298928ffdccb03b0715 Mon Sep 17 00:00:00 2001 From: TheKodeToad <TheKodeToad@proton.me> Date: Tue, 3 Oct 2023 01:26:38 +0100 Subject: [PATCH 09/38] Fix WhoReacted (#1769) --- src/plugins/whoReacted/README.md | 5 ++ src/plugins/whoReacted/index.tsx | 119 ++++++------------------------- 2 files changed, 26 insertions(+), 98 deletions(-) create mode 100644 src/plugins/whoReacted/README.md diff --git a/src/plugins/whoReacted/README.md b/src/plugins/whoReacted/README.md new file mode 100644 index 00000000..ee4227ab --- /dev/null +++ b/src/plugins/whoReacted/README.md @@ -0,0 +1,5 @@ +# WhoReacted + +Next to each reaction, display each user's avatar. Each avatar can be clicked and will open the profile. + +![](https://github.com/Vendicated/Vencord/assets/57493648/97fec9e8-396f-4f5e-916e-1ec21445113d) diff --git a/src/plugins/whoReacted/index.tsx b/src/plugins/whoReacted/index.tsx index 0bdb5c27..a4e74f94 100644 --- a/src/plugins/whoReacted/index.tsx +++ b/src/plugins/whoReacted/index.tsx @@ -24,14 +24,14 @@ import { LazyComponent, useForceUpdater } from "@utils/react"; import definePlugin from "@utils/types"; import { findByCode, findByPropsLazy } from "@webpack"; import { ChannelStore, FluxDispatcher, React, RestAPI, Tooltip } from "@webpack/common"; -import { ReactionEmoji, User } from "discord-types/general"; +import { CustomEmoji } from "@webpack/types"; +import { Message, ReactionEmoji, User } from "discord-types/general"; const UserSummaryItem = LazyComponent(() => findByCode("defaultRenderUser", "showDefaultAvatarsForNullUsers")); const AvatarStyles = findByPropsLazy("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar"); -const ReactionStore = findByPropsLazy("getReactions"); - const queue = new Queue(); +let reactions: Record<string, ReactionCacheEntry>; function fetchReactions(msg: Message, emoji: ReactionEmoji, type: number) { const key = emoji.name + (emoji.id ? `:${emoji.id}` : ""); @@ -57,11 +57,9 @@ function fetchReactions(msg: Message, emoji: ReactionEmoji, type: number) { function getReactionsWithQueue(msg: Message, e: ReactionEmoji, type: number) { const key = `${msg.id}:${e.name}:${e.id ?? ""}:${type}`; - const cache = ReactionStore.__getLocalVars().reactions[key] ??= { fetched: false, users: {} }; + const cache = reactions[key] ??= { fetched: false, users: {} }; if (!cache.fetched) { - queue.unshift(() => - fetchReactions(msg, e, type) - ); + queue.unshift(() => fetchReactions(msg, e, type)); cache.fetched = true; } @@ -92,7 +90,7 @@ function handleClickAvatar(event: React.MouseEvent<HTMLElement, MouseEvent>) { export default definePlugin({ name: "WhoReacted", - description: "Renders the Avatars of reactors", + description: "Renders the avatars of users who reacted to a message", authors: [Devs.Ven, Devs.KannaDev], patches: [{ @@ -101,6 +99,12 @@ export default definePlugin({ match: /(?<=(\i)=(\i)\.hideCount,)(.+?reactionCount.+?\}\))/, replace: (_, hideCount, props, rest) => `whoReactedProps=${props},${rest},${hideCount}?null:$self.renderUsers(whoReactedProps)` } + }, { + find: '.displayName="MessageReactionsStore";', + replacement: { + match: /(?<=CONNECTION_OPEN:function\(\){)(\i)={}/, + replace: "$&;$self.reactions=$1" + } }], renderUsers(props: RootObject) { @@ -150,106 +154,25 @@ export default definePlugin({ </div> </div> ); + }, + + set reactions(value: any) { + reactions = value; } }); - -export interface GuildMemberAvatar { } - -export interface Author { - id: string; - username: string; - discriminator: string; - avatar: string; - avatarDecoration?: any; - email: string; - verified: boolean; - bot: boolean; - system: boolean; - mfaEnabled: boolean; - mobile: boolean; - desktop: boolean; - premiumType: number; - flags: number; - publicFlags: number; - purchasedFlags: number; - premiumUsageFlags: number; - phone: string; - nsfwAllowed: boolean; - guildMemberAvatars: GuildMemberAvatar; +interface ReactionCacheEntry { + fetched: boolean; + users: Record<string, User>; } -export interface Emoji { - id: string; - name: string; -} - -export interface Reaction { - emoji: Emoji; - count: number; - burst_user_ids: any[]; - burst_count: number; - burst_colors: any[]; - burst_me: boolean; - me: boolean; -} - -export interface Message { - id: string; - type: number; - channel_id: string; - author: Author; - content: string; - deleted: boolean; - editHistory: any[]; - attachments: any[]; - embeds: any[]; - mentions: any[]; - mentionRoles: any[]; - mentionChannels: any[]; - mentioned: boolean; - pinned: boolean; - mentionEveryone: boolean; - tts: boolean; - codedLinks: any[]; - giftCodes: any[]; - timestamp: string; - editedTimestamp?: any; - state: string; - nonce?: any; - blocked: boolean; - call?: any; - bot: boolean; - webhookId?: any; - reactions: Reaction[]; - applicationId?: any; - application?: any; - activity?: any; - messageReference?: any; - flags: number; - isSearchHit: boolean; - stickers: any[]; - stickerItems: any[]; - components: any[]; - loggingName?: any; - interaction?: any; - interactionData?: any; - interactionError?: any; -} - -export interface Emoji { - id: string; - name: string; - animated: boolean; -} - -export interface RootObject { +interface RootObject { message: Message; readOnly: boolean; isLurking: boolean; isPendingMember: boolean; useChatFontScaling: boolean; - emoji: Emoji; + emoji: CustomEmoji; count: number; burst_user_ids: any[]; burst_count: number; From 30b2e88e7755691fbcc41e6046761532b4e0a7f7 Mon Sep 17 00:00:00 2001 From: Vendicated <vendicated@riseup.net> Date: Tue, 3 Oct 2023 02:29:57 +0200 Subject: [PATCH 10/38] Bump to v1.5.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3fcac350..13481d01 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vencord", "private": "true", - "version": "1.5.3", + "version": "1.5.4", "description": "The cutest Discord client mod", "homepage": "https://github.com/Vendicated/Vencord#readme", "bugs": { From 581fe252a44d7ba24f277cc9d28abd7a8f5d6729 Mon Sep 17 00:00:00 2001 From: AutumnVN <autumnvnchino@gmail.com> Date: Tue, 3 Oct 2023 07:39:34 +0700 Subject: [PATCH 11/38] fix imageZoom (#1772) --- src/plugins/imageZoom/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/imageZoom/index.tsx b/src/plugins/imageZoom/index.tsx index cca0db02..60f8a22c 100644 --- a/src/plugins/imageZoom/index.tsx +++ b/src/plugins/imageZoom/index.tsx @@ -165,7 +165,7 @@ export default definePlugin({ { find: '"renderLinkComponent","maxWidth"', replacement: { - match: /(return\(.{1,100}\(\)\.wrapper.{1,100})(src)/, + match: /(return\(.{1,100}\(\)\.wrapper.{1,200})(src)/, replace: `$1id: '${ELEMENT_ID}',$2` } }, From 726a1b5d96eef2f32cba56a45ac51961c7f4ba07 Mon Sep 17 00:00:00 2001 From: wntiv-main <60457971+wntiv-main@users.noreply.github.com> Date: Fri, 6 Oct 2023 14:16:21 +1300 Subject: [PATCH 12/38] Fix command API (#1776) Co-authored-by: V <vendicated@riseup.net> --- src/plugins/_api/commands.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/_api/commands.ts b/src/plugins/_api/commands.ts index 2197b307..b7d6ef9f 100644 --- a/src/plugins/_api/commands.ts +++ b/src/plugins/_api/commands.ts @@ -26,7 +26,7 @@ export default definePlugin({ patches: [ // obtain BUILT_IN_COMMANDS instance { - find: '"giphy","tenor"', + find: ',"tenor"', replacement: [ { // Matches BUILT_IN_COMMANDS. This is not exported so this is @@ -34,7 +34,7 @@ export default definePlugin({ // patch simpler // textCommands = builtInCommands.filter(...) - match: /(?<=\w=)(\w)(\.filter\(.{0,30}giphy)/, + match: /(?<=\w=)(\w)(\.filter\(.{0,60}tenor)/, replace: "Vencord.Api.Commands._init($1)$2", } ], From 03b5dc9c27250be09ae1a8c80842edf8cde642d9 Mon Sep 17 00:00:00 2001 From: Vendicated <vendicated@riseup.net> Date: Fri, 6 Oct 2023 03:17:44 +0200 Subject: [PATCH 13/38] BetterRoleDot: Fix ci test false positives --- src/plugins/betterRoleDot/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/betterRoleDot/index.ts b/src/plugins/betterRoleDot/index.ts index e8f69b89..ed121b9a 100644 --- a/src/plugins/betterRoleDot/index.ts +++ b/src/plugins/betterRoleDot/index.ts @@ -48,6 +48,7 @@ export default definePlugin({ { find: ".ADD_ROLE_A11Y_LABEL", predicate: () => Settings.plugins.BetterRoleDot.copyRoleColorInProfilePopout && !Settings.plugins.BetterRoleDot.bothStyles, + noWarn: true, replacement: { match: /"dot"===\i/, replace: "true" @@ -56,6 +57,7 @@ export default definePlugin({ { find: ".roleVerifiedIcon", predicate: () => Settings.plugins.BetterRoleDot.copyRoleColorInProfilePopout && !Settings.plugins.BetterRoleDot.bothStyles, + noWarn: true, replacement: { match: /"dot"===\i/, replace: "true" From 5eb9dd04dff212686e3395752877ddf62c1f78e2 Mon Sep 17 00:00:00 2001 From: Vendicated <vendicated@riseup.net> Date: Fri, 6 Oct 2023 04:00:09 +0200 Subject: [PATCH 14/38] Fix member list decorations api --- src/api/MemberListDecorators.ts | 5 ++--- src/plugins/_api/memberListDecorators.ts | 21 ++++++++++++++------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/api/MemberListDecorators.ts b/src/api/MemberListDecorators.ts index fade2a7c..e148bb0a 100644 --- a/src/api/MemberListDecorators.ts +++ b/src/api/MemberListDecorators.ts @@ -20,7 +20,6 @@ import { Channel, User } from "discord-types/general/index.js"; interface DecoratorProps { activities: any[]; - canUseAvatarDecorations: boolean; channel: Channel; /** * Only for DM members @@ -52,9 +51,9 @@ export function removeDecorator(identifier: string) { decorators.delete(identifier); } -export function __addDecoratorsToList(props: DecoratorProps): (JSX.Element | null)[] { +export function __getDecorators(props: DecoratorProps): (JSX.Element | null)[] { const isInGuild = !!(props.guildId); - return [...decorators.values()].map(decoratorObj => { + return Array.from(decorators.values(), decoratorObj => { const { decorator, onlyIn } = decoratorObj; // this can most likely be done cleaner if (!onlyIn || (onlyIn === "guilds" && isInGuild) || (onlyIn === "dms" && !isInGuild)) { diff --git a/src/plugins/_api/memberListDecorators.ts b/src/plugins/_api/memberListDecorators.ts index 6b8cffab..a6d4125d 100644 --- a/src/plugins/_api/memberListDecorators.ts +++ b/src/plugins/_api/memberListDecorators.ts @@ -22,21 +22,28 @@ import definePlugin from "@utils/types"; export default definePlugin({ name: "MemberListDecoratorsAPI", description: "API to add decorators to member list (both in servers and DMs)", - authors: [Devs.TheSun], + authors: [Devs.TheSun, Devs.Ven], patches: [ { find: "lostPermissionTooltipText,", replacement: { - match: /Fragment,{children:\[(.{30,80})\]/, - replace: "Fragment,{children:Vencord.Api.MemberListDecorators.__addDecoratorsToList(this.props).concat($1)" + match: /decorators:.{0,100}?children:\[(?<=(\i)\.lostPermissionTooltipText.+?)/, + replace: "$&...Vencord.Api.MemberListDecorators.__getDecorators($1)," } }, { find: "PrivateChannel.renderAvatar", - replacement: { - match: /(subText:(.{1,2})\.renderSubtitle\(\).{1,50}decorators):(.{30,100}:null)/, - replace: "$1:Vencord.Api.MemberListDecorators.__addDecoratorsToList($2.props).concat($3)" - } + replacement: [ + // props are shadowed by nested props so we have to do this + { + match: /\i=(\i)\.applicationStream,/, + replace: "$&vencordProps=$1," + }, + { + match: /decorators:(\i\.isSystemDM\(\))\?(.+?):null/, + replace: "decorators:[...(typeof vencordProps=='undefined'?[]:Vencord.Api.MemberListDecorators.__getDecorators(vencordProps)), $1?$2:null]" + } + ] } ], }); From 79295683eeb513de7cf07cf647bf56de5cae37aa Mon Sep 17 00:00:00 2001 From: AutumnVN <autumnvnchino@gmail.com> Date: Fri, 6 Oct 2023 09:07:16 +0700 Subject: [PATCH 15/38] PictureInPicture: pip button hover styles (#1775) Co-authored-by: V <vendicated@riseup.net> --- src/plugins/pictureInPicture/index.tsx | 5 ++++- src/plugins/pictureInPicture/styles.css | 8 ++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 src/plugins/pictureInPicture/styles.css diff --git a/src/plugins/pictureInPicture/index.tsx b/src/plugins/pictureInPicture/index.tsx index d10d42ff..bb6aee18 100644 --- a/src/plugins/pictureInPicture/index.tsx +++ b/src/plugins/pictureInPicture/index.tsx @@ -4,6 +4,8 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ +import "./styles.css"; + import { definePluginSettings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; @@ -41,6 +43,7 @@ export default definePlugin({ {tooltipProps => ( <div {...tooltipProps} + className="vc-pip-button" role="button" style={{ cursor: "pointer", @@ -71,7 +74,7 @@ export default definePlugin({ > <svg width="24px" height="24px" viewBox="0 0 24 24"> <path - fill="var(--interactive-normal)" + fill="currentColor" d="M21 3a1 1 0 0 1 1 1v7h-2V5H4v14h6v2H3a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h18zm0 10a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1h-8a1 1 0 0 1-1-1v-6a1 1 0 0 1 1-1h8zm-1 2h-6v4h6v-4z" /> </svg> diff --git a/src/plugins/pictureInPicture/styles.css b/src/plugins/pictureInPicture/styles.css new file mode 100644 index 00000000..d0a81ca5 --- /dev/null +++ b/src/plugins/pictureInPicture/styles.css @@ -0,0 +1,8 @@ +.vc-pip-button { + color: var(--interactive-normal); +} + +.vc-pip-button:hover { + background-color: var(--background-modifier-hover); + color: var(--interactive-hover); +} From 9e63da6d786b36cc27c4a24791f44635ba1e0c23 Mon Sep 17 00:00:00 2001 From: Vendicated <vendicated@riseup.net> Date: Fri, 6 Oct 2023 04:08:49 +0200 Subject: [PATCH 16/38] bump to v1.5.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 13481d01..c005800a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vencord", "private": "true", - "version": "1.5.4", + "version": "1.5.5", "description": "The cutest Discord client mod", "homepage": "https://github.com/Vendicated/Vencord#readme", "bugs": { From 47a39a062e4437c3e18b1f5cb09f4abe82d0bb82 Mon Sep 17 00:00:00 2001 From: Vendicated <vendicated@riseup.net> Date: Fri, 6 Oct 2023 17:54:46 +0200 Subject: [PATCH 17/38] Fix Vesktop SettingsCog context menu --- src/plugins/_core/settings.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/plugins/_core/settings.tsx b/src/plugins/_core/settings.tsx index 88ebb578..f7fcdd0a 100644 --- a/src/plugins/_core/settings.tsx +++ b/src/plugins/_core/settings.tsx @@ -39,8 +39,9 @@ export default definePlugin({ addContextMenuPatch("user-settings-cog", children => () => { const section = children.find(c => Array.isArray(c) && c.some(it => it?.props?.id === "VencordSettings")) as any; section?.forEach(c => { - if (c?.props?.id?.startsWith("Vencord")) { - c.props.action = () => SettingsRouter.open(c.props.id); + const id = c?.props?.id; + if (id?.startsWith("Vencord") || id?.startsWith("Vesktop")) { + c.props.action = () => SettingsRouter.open(id); } }); }); From df214e1e93bf7bc6c7b768df75f10f764a41ff13 Mon Sep 17 00:00:00 2001 From: Vendicated <vendicated@riseup.net> Date: Fri, 6 Oct 2023 18:01:19 +0200 Subject: [PATCH 18/38] chore: remove legacy code --- src/plugins/fakeNitro/index.ts | 3 +-- src/plugins/pronoundb/index.ts | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/plugins/fakeNitro/index.ts b/src/plugins/fakeNitro/index.ts index 07191e14..a11a43de 100644 --- a/src/plugins/fakeNitro/index.ts +++ b/src/plugins/fakeNitro/index.ts @@ -222,8 +222,7 @@ export default definePlugin({ predicate: () => settings.store.enableStreamQualityBypass, replacement: [ "canUseHighVideoUploadQuality", - // TODO: Remove the last two when they get removed from stable - "(?:canStreamQuality|canStreamHighQuality|canStreamMidQuality)", + "canStreamQuality", ].map(func => { return { match: new RegExp(`${func}:function\\(\\i(?:,\\i)?\\){`, "g"), diff --git a/src/plugins/pronoundb/index.ts b/src/plugins/pronoundb/index.ts index 52fefdc3..c9dc2725 100644 --- a/src/plugins/pronoundb/index.ts +++ b/src/plugins/pronoundb/index.ts @@ -68,8 +68,7 @@ export default definePlugin({ find: ".USER_PROFILE_ACTIVITY", replacement: [ { - /* FIXME: old name is getGlobalName, new name is getName. Remove optional Global once stable has also migrated */ - match: /\.get(?:Global)?Name\(\i\);(?<=displayProfile.{0,200})/, + match: /\.getName\(\i\);(?<=displayProfile.{0,200})/, replace: "$&const [vcPronounce,vcPronounSource]=$self.useProfilePronouns(arguments[0].user.id,true);if(arguments[0].displayProfile&&vcPronounce)arguments[0].displayProfile.pronouns=vcPronounce;" }, PRONOUN_TOOLTIP_PATCH From f66e35b65875e7669e5ccdb2710d83f10f41baa3 Mon Sep 17 00:00:00 2001 From: Vendicated <vendicated@riseup.net> Date: Fri, 6 Oct 2023 18:05:53 +0200 Subject: [PATCH 19/38] fix(SendTimestamps): Do not add to ReviewDB input --- src/plugins/sendTimestamps/index.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/plugins/sendTimestamps/index.tsx b/src/plugins/sendTimestamps/index.tsx index a69136ba..a69dbacc 100644 --- a/src/plugins/sendTimestamps/index.tsx +++ b/src/plugins/sendTimestamps/index.tsx @@ -124,7 +124,7 @@ export default definePlugin({ find: ".activeCommandOption", replacement: { match: /(.)\.push.{1,30}disabled:(\i),.{1,20}\},"gift"\)\)/, - replace: "$&;try{$2||$1.push($self.chatBarIcon())}catch{}", + replace: "$&;try{$2||$1.push($self.chatBarIcon(arguments[0]))}catch{}", } }, ], @@ -139,7 +139,9 @@ export default definePlugin({ removePreSendListener(this.listener); }, - chatBarIcon() { + chatBarIcon(chatBoxProps: { type: { analyticsName: string; }; }) { + if (chatBoxProps.type.analyticsName !== "normal") return null; + return ( <Tooltip text="Insert Timestamp"> {({ onMouseEnter, onMouseLeave }) => ( From 664dd0a9920aa697359b1bb07b98795ff0f1beaf Mon Sep 17 00:00:00 2001 From: Vendicated <vendicated@riseup.net> Date: Fri, 6 Oct 2023 18:44:22 +0200 Subject: [PATCH 20/38] ReviewDB: allow deleting reviews on own profile --- src/plugins/reviewDB/components/ReviewComponent.tsx | 8 ++++---- src/plugins/reviewDB/components/ReviewsView.tsx | 4 +++- src/plugins/reviewDB/utils.tsx | 8 +++++--- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/plugins/reviewDB/components/ReviewComponent.tsx b/src/plugins/reviewDB/components/ReviewComponent.tsx index 07bcdb28..57924001 100644 --- a/src/plugins/reviewDB/components/ReviewComponent.tsx +++ b/src/plugins/reviewDB/components/ReviewComponent.tsx @@ -20,7 +20,7 @@ import { openUserProfile } from "@utils/discord"; import { classes } from "@utils/misc"; import { LazyComponent } from "@utils/react"; import { filters, findBulk } from "@webpack"; -import { Alerts, moment, Parser, Timestamp, UserStore } from "@webpack/common"; +import { Alerts, moment, Parser, Timestamp } from "@webpack/common"; import { Review, ReviewType } from "../entities"; import { deleteReview, reportReview } from "../reviewDbApi"; @@ -30,7 +30,7 @@ import { DeleteButton, ReportButton } from "./MessageButton"; import ReviewBadge from "./ReviewBadge"; export default LazyComponent(() => { - // this is terrible, blame ven + // this is terrible, blame mantika const p = filters.byProps; const [ { cozyMessage, buttons, message, buttonsInner, groupStart }, @@ -48,7 +48,7 @@ export default LazyComponent(() => { const dateFormat = new Intl.DateTimeFormat(); - return function ReviewComponent({ review, refetch }: { review: Review; refetch(): void; }) { + return function ReviewComponent({ review, refetch, profileId }: { review: Review; refetch(): void; profileId: string; }) { function openModal() { openUserProfile(review.sender.discordID); } @@ -135,7 +135,7 @@ export default LazyComponent(() => { <div className={classes(buttonClasses.wrapper, buttonsInner)} > <ReportButton onClick={reportRev} /> - {canDeleteReview(review, UserStore.getCurrentUser().id) && ( + {canDeleteReview(profileId, review) && ( <DeleteButton onClick={delReview} /> )} </div> diff --git a/src/plugins/reviewDB/components/ReviewsView.tsx b/src/plugins/reviewDB/components/ReviewsView.tsx index e5bc426d..5eb370f4 100644 --- a/src/plugins/reviewDB/components/ReviewsView.tsx +++ b/src/plugins/reviewDB/components/ReviewsView.tsx @@ -79,6 +79,7 @@ export default function ReviewsView({ refetch={refetch} reviews={reviewData!.reviews} hideOwnReview={hideOwnReview} + profileId={discordId} /> {showInput && ( @@ -93,7 +94,7 @@ export default function ReviewsView({ ); } -function ReviewList({ refetch, reviews, hideOwnReview }: { refetch(): void; reviews: Review[]; hideOwnReview: boolean; }) { +function ReviewList({ refetch, reviews, hideOwnReview, profileId }: { refetch(): void; reviews: Review[]; hideOwnReview: boolean; profileId: string; }) { const myId = UserStore.getCurrentUser().id; return ( @@ -104,6 +105,7 @@ function ReviewList({ refetch, reviews, hideOwnReview }: { refetch(): void; revi key={review.id} review={review} refetch={refetch} + profileId={profileId} /> )} diff --git a/src/plugins/reviewDB/utils.tsx b/src/plugins/reviewDB/utils.tsx index a9c8ca57..63ab84dd 100644 --- a/src/plugins/reviewDB/utils.tsx +++ b/src/plugins/reviewDB/utils.tsx @@ -20,7 +20,7 @@ import { classNameFactory } from "@api/Styles"; import { Logger } from "@utils/Logger"; import { openModal } from "@utils/modal"; import { findByProps } from "@webpack"; -import { React, Toasts } from "@webpack/common"; +import { React, Toasts, UserStore } from "@webpack/common"; import { Review, UserType } from "./entities"; import { settings } from "./settings"; @@ -73,9 +73,11 @@ export function showToast(text: string) { }); } -export function canDeleteReview(review: Review, userId: string) { +export function canDeleteReview(profileId: string, review: Review) { + const myId = UserStore.getCurrentUser().id; return ( - review.sender.discordID === userId + myId === profileId + || review.sender.discordID === profileId || settings.store.user?.type === UserType.Admin ); } From c0f2c974587d75a38e3e753368ef0e2e2be139fd Mon Sep 17 00:00:00 2001 From: Vendicated <vendicated@riseup.net> Date: Fri, 6 Oct 2023 19:40:53 +0200 Subject: [PATCH 21/38] ReviewDB: proper multi account support --- src/plugins/reviewDB/auth.tsx | 78 +++++++++++++++++++ .../reviewDB/components/ReviewComponent.tsx | 4 +- .../reviewDB/components/ReviewModal.tsx | 5 +- .../reviewDB/components/ReviewsView.tsx | 7 +- src/plugins/reviewDB/entities.ts | 5 ++ src/plugins/reviewDB/index.tsx | 49 ++++++++---- src/plugins/reviewDB/reviewDbApi.ts | 18 +++-- src/plugins/reviewDB/settings.tsx | 12 ++- src/plugins/reviewDB/utils.tsx | 55 +------------ 9 files changed, 143 insertions(+), 90 deletions(-) create mode 100644 src/plugins/reviewDB/auth.tsx diff --git a/src/plugins/reviewDB/auth.tsx b/src/plugins/reviewDB/auth.tsx new file mode 100644 index 00000000..1d95e47d --- /dev/null +++ b/src/plugins/reviewDB/auth.tsx @@ -0,0 +1,78 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2023 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { DataStore } from "@api/index"; +import { Logger } from "@utils/Logger"; +import { openModal } from "@utils/modal"; +import { findByPropsLazy } from "@webpack"; +import { showToast, Toasts, UserStore } from "@webpack/common"; + +import { ReviewDBAuth } from "./entities"; + +const DATA_STORE_KEY = "rdb-auth"; + +const OAuth = findByPropsLazy("OAuth2AuthorizeModal"); + +export let Auth: ReviewDBAuth = {}; + +export async function initAuth() { + Auth = await getAuth() ?? {}; +} + +export async function getAuth(): Promise<ReviewDBAuth | undefined> { + const auth = await DataStore.get(DATA_STORE_KEY); + return auth?.[UserStore.getCurrentUser()?.id]; +} + +export async function getToken() { + const auth = await getAuth(); + return auth?.token; +} + +export async function updateAuth(newAuth: ReviewDBAuth) { + return DataStore.update(DATA_STORE_KEY, auth => { + auth ??= {}; + Auth = auth[UserStore.getCurrentUser().id] ??= {}; + + if (newAuth.token) Auth.token = newAuth.token; + if (newAuth.user) Auth.user = newAuth.user; + + return auth; + }); +} + +export function authorize(callback?: any) { + openModal(props => + <OAuth.OAuth2AuthorizeModal + {...props} + scopes={["identify"]} + responseType="code" + redirectUri="https://manti.vendicated.dev/api/reviewdb/auth" + permissions={0n} + clientId="915703782174752809" + cancelCompletesFlow={false} + callback={async (response: any) => { + try { + const url = new URL(response.location); + url.searchParams.append("clientMod", "vencord"); + const res = await fetch(url, { + headers: new Headers({ Accept: "application/json" }) + }); + const { token, success } = await res.json(); + if (success) { + updateAuth({ token }); + showToast("Successfully logged in!"); + callback?.(); + } else if (res.status === 1) { + showToast("An Error occurred while logging in.", Toasts.Type.FAILURE); + } + } catch (e) { + new Logger("ReviewDB").error("Failed to authorize", e); + } + }} + /> + ); +} diff --git a/src/plugins/reviewDB/components/ReviewComponent.tsx b/src/plugins/reviewDB/components/ReviewComponent.tsx index 57924001..18659179 100644 --- a/src/plugins/reviewDB/components/ReviewComponent.tsx +++ b/src/plugins/reviewDB/components/ReviewComponent.tsx @@ -20,12 +20,12 @@ import { openUserProfile } from "@utils/discord"; import { classes } from "@utils/misc"; import { LazyComponent } from "@utils/react"; import { filters, findBulk } from "@webpack"; -import { Alerts, moment, Parser, Timestamp } from "@webpack/common"; +import { Alerts, moment, Parser, showToast, Timestamp } from "@webpack/common"; import { Review, ReviewType } from "../entities"; import { deleteReview, reportReview } from "../reviewDbApi"; import { settings } from "../settings"; -import { canDeleteReview, cl, showToast } from "../utils"; +import { canDeleteReview, cl } from "../utils"; import { DeleteButton, ReportButton } from "./MessageButton"; import ReviewBadge from "./ReviewBadge"; diff --git a/src/plugins/reviewDB/components/ReviewModal.tsx b/src/plugins/reviewDB/components/ReviewModal.tsx index 6e85dc29..9669a2b3 100644 --- a/src/plugins/reviewDB/components/ReviewModal.tsx +++ b/src/plugins/reviewDB/components/ReviewModal.tsx @@ -21,8 +21,8 @@ import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalRoot, Mo import { useForceUpdater } from "@utils/react"; import { Paginator, Text, useRef, useState } from "@webpack/common"; +import { Auth } from "../auth"; import { Response, REVIEWS_PER_PAGE } from "../reviewDbApi"; -import { settings } from "../settings"; import { cl } from "../utils"; import ReviewComponent from "./ReviewComponent"; import ReviewsView, { ReviewsInputComponent } from "./ReviewsView"; @@ -35,7 +35,7 @@ function Modal({ modalProps, discordId, name }: { modalProps: any; discordId: st const ref = useRef<HTMLDivElement>(null); const reviewCount = data?.reviewCount; - const ownReview = data?.reviews.find(r => r.sender.discordID === settings.store.user?.discordID); + const ownReview = data?.reviews.find(r => r.sender.discordID === Auth.user?.discordID); return ( <ErrorBoundary> @@ -68,6 +68,7 @@ function Modal({ modalProps, discordId, name }: { modalProps: any; discordId: st <ReviewComponent refetch={refetch} review={ownReview} + profileId={discordId} /> )} <ReviewsInputComponent diff --git a/src/plugins/reviewDB/components/ReviewsView.tsx b/src/plugins/reviewDB/components/ReviewsView.tsx index 5eb370f4..a87598be 100644 --- a/src/plugins/reviewDB/components/ReviewsView.tsx +++ b/src/plugins/reviewDB/components/ReviewsView.tsx @@ -18,12 +18,13 @@ import { LazyComponent, useAwaiter, useForceUpdater } from "@utils/react"; import { find, findByPropsLazy } from "@webpack"; -import { Forms, React, RelationshipStore, useRef, UserStore } from "@webpack/common"; +import { Forms, React, RelationshipStore, showToast, useRef, UserStore } from "@webpack/common"; +import { Auth, authorize } from "../auth"; import { Review } from "../entities"; import { addReview, getReviews, Response, REVIEWS_PER_PAGE } from "../reviewDbApi"; import { settings } from "../settings"; -import { authorize, cl, showToast } from "../utils"; +import { cl } from "../utils"; import ReviewComponent from "./ReviewComponent"; @@ -120,7 +121,7 @@ function ReviewList({ refetch, reviews, hideOwnReview, profileId }: { refetch(): export function ReviewsInputComponent({ discordId, isAuthor, refetch, name }: { discordId: string, name: string; isAuthor: boolean; refetch(): void; }) { - const { token } = settings.store; + const { token } = Auth; const editorRef = useRef<any>(null); const inputType = InputTypes.FORM; inputType.disableAutoFocus = true; diff --git a/src/plugins/reviewDB/entities.ts b/src/plugins/reviewDB/entities.ts index fefb1d3f..0a77fef0 100644 --- a/src/plugins/reviewDB/entities.ts +++ b/src/plugins/reviewDB/entities.ts @@ -36,6 +36,11 @@ export const enum NotificationType { Warning = 3 } +export interface ReviewDBAuth { + token?: string; + user?: ReviewDBUser; +} + export interface Badge { name: string; description: string; diff --git a/src/plugins/reviewDB/index.tsx b/src/plugins/reviewDB/index.tsx index a5197139..b9350ba4 100644 --- a/src/plugins/reviewDB/index.tsx +++ b/src/plugins/reviewDB/index.tsx @@ -23,16 +23,17 @@ import ErrorBoundary from "@components/ErrorBoundary"; import ExpandableHeader from "@components/ExpandableHeader"; import { OpenExternalIcon } from "@components/Icons"; import { Devs } from "@utils/constants"; +import { Logger } from "@utils/Logger"; import definePlugin from "@utils/types"; -import { Alerts, Menu, Parser, useState } from "@webpack/common"; +import { Alerts, Menu, Parser, showToast, useState } from "@webpack/common"; import { Guild, User } from "discord-types/general"; +import { Auth, initAuth, updateAuth } from "./auth"; import { openReviewsModal } from "./components/ReviewModal"; import ReviewsView from "./components/ReviewsView"; import { NotificationType } from "./entities"; import { getCurrentUserInfo, readNotification } from "./reviewDbApi"; import { settings } from "./settings"; -import { showToast } from "./utils"; const guildPopoutPatch: NavContextMenuPatchCallback = (children, props: { guild: Guild, onClose(): void; }) => () => { children.push( @@ -62,31 +63,48 @@ export default definePlugin({ } ], - async start() { - const s = settings.store; - const { token, lastReviewId, notifyReviews } = s; + flux: { + CONNECTION_OPEN: initAuth, + }, - if (!notifyReviews || !token) return; + async start() { + addContextMenuPatch("guild-header-popout", guildPopoutPatch); + + const s = settings.store; + const { lastReviewId, notifyReviews } = s; + + const legacy = s as any as { token?: string; }; + if (legacy.token) { + await updateAuth({ token: legacy.token }); + legacy.token = undefined; + new Logger("ReviewDB").info("Migrated legacy settings"); + } + + await initAuth(); setTimeout(async () => { - const user = await getCurrentUserInfo(token); - if (lastReviewId && lastReviewId < user.lastReviewID) { - s.lastReviewId = user.lastReviewID; - if (user.lastReviewID !== 0) - showToast("You have new reviews on your profile!"); - } + if (!Auth.token) return; - addContextMenuPatch("guild-header-popout", guildPopoutPatch); + const user = await getCurrentUserInfo(Auth.token); + updateAuth({ user }); + + if (notifyReviews) { + if (lastReviewId && lastReviewId < user.lastReviewID) { + s.lastReviewId = user.lastReviewID; + if (user.lastReviewID !== 0) + showToast("You have new reviews on your profile!"); + } + } if (user.notification) { const props = user.notification.type === NotificationType.Ban ? { cancelText: "Appeal", confirmText: "Ok", - onCancel: () => + onCancel: async () => VencordNative.native.openExternal( "https://reviewdb.mantikafasi.dev/api/redirect?" + new URLSearchParams({ - token: settings.store.token!, + token: Auth.token!, page: "dashboard/appeal" }) ) @@ -105,7 +123,6 @@ export default definePlugin({ readNotification(user.notification.id); } - s.user = user; }, 4000); }, diff --git a/src/plugins/reviewDB/reviewDbApi.ts b/src/plugins/reviewDB/reviewDbApi.ts index 5370a9b3..add16dd1 100644 --- a/src/plugins/reviewDB/reviewDbApi.ts +++ b/src/plugins/reviewDB/reviewDbApi.ts @@ -16,9 +16,11 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ +import { showToast, Toasts } from "@webpack/common"; + +import { authorize, getToken } from "./auth"; import { Review, ReviewDBUser } from "./entities"; import { settings } from "./settings"; -import { authorize, showToast } from "./utils"; const API_URL = "https://manti.vendicated.dev"; @@ -57,7 +59,7 @@ export async function getReviews(id: string, offset = 0): Promise<Response> { }; if (!res.success) { - showToast(res.message); + showToast(res.message, Toasts.Type.FAILURE); return { ...res, reviews: [ @@ -82,7 +84,7 @@ export async function getReviews(id: string, offset = 0): Promise<Response> { } export async function addReview(review: any): Promise<Response | null> { - review.token = settings.store.token; + review.token = await getToken(); if (!review.token) { showToast("Please authorize to add a review."); @@ -104,7 +106,7 @@ export async function addReview(review: any): Promise<Response | null> { }); } -export function deleteReview(id: number): Promise<Response> { +export async function deleteReview(id: number): Promise<Response> { return fetch(API_URL + `/api/reviewdb/users/${id}/reviews`, { method: "DELETE", headers: new Headers({ @@ -112,7 +114,7 @@ export function deleteReview(id: number): Promise<Response> { Accept: "application/json", }), body: JSON.stringify({ - token: settings.store.token, + token: await getToken(), reviewid: id }) }).then(r => r.json()); @@ -127,7 +129,7 @@ export async function reportReview(id: number) { }), body: JSON.stringify({ reviewid: id, - token: settings.store.token + token: await getToken() }) }).then(r => r.json()) as Response; @@ -141,11 +143,11 @@ export function getCurrentUserInfo(token: string): Promise<ReviewDBUser> { }).then(r => r.json()); } -export function readNotification(id: number) { +export async function readNotification(id: number) { return fetch(API_URL + `/api/reviewdb/notifications?id=${id}`, { method: "PATCH", headers: { - "Authorization": settings.store.token || "", + "Authorization": await getToken() || "", }, }); } diff --git a/src/plugins/reviewDB/settings.tsx b/src/plugins/reviewDB/settings.tsx index e318bc7b..cf61a380 100644 --- a/src/plugins/reviewDB/settings.tsx +++ b/src/plugins/reviewDB/settings.tsx @@ -20,8 +20,7 @@ import { definePluginSettings } from "@api/Settings"; import { OptionType } from "@utils/types"; import { Button } from "@webpack/common"; -import { ReviewDBUser } from "./entities"; -import { authorize } from "./utils"; +import { authorize, getToken } from "./auth"; export const settings = definePluginSettings({ authorize: { @@ -57,10 +56,11 @@ export const settings = definePluginSettings({ type: OptionType.COMPONENT, description: "ReviewDB website", component: () => ( - <Button onClick={() => { + <Button onClick={async () => { let url = "https://reviewdb.mantikafasi.dev/"; - if (settings.store.token) - url += "/api/redirect?token=" + encodeURIComponent(settings.store.token); + const token = await getToken(); + if (token) + url += "/api/redirect?token=" + encodeURIComponent(token); VencordNative.native.openExternal(url); }}> @@ -80,8 +80,6 @@ export const settings = definePluginSettings({ ) } }).withPrivateSettings<{ - token?: string; - user?: ReviewDBUser; lastReviewId?: number; reviewsDropdownState?: boolean; }>(); diff --git a/src/plugins/reviewDB/utils.tsx b/src/plugins/reviewDB/utils.tsx index 63ab84dd..ab66d531 100644 --- a/src/plugins/reviewDB/utils.tsx +++ b/src/plugins/reviewDB/utils.tsx @@ -17,67 +17,18 @@ */ import { classNameFactory } from "@api/Styles"; -import { Logger } from "@utils/Logger"; -import { openModal } from "@utils/modal"; -import { findByProps } from "@webpack"; -import { React, Toasts, UserStore } from "@webpack/common"; +import { UserStore } from "@webpack/common"; +import { Auth } from "./auth"; import { Review, UserType } from "./entities"; -import { settings } from "./settings"; export const cl = classNameFactory("vc-rdb-"); -export function authorize(callback?: any) { - const { OAuth2AuthorizeModal } = findByProps("OAuth2AuthorizeModal"); - - openModal((props: any) => - <OAuth2AuthorizeModal - {...props} - scopes={["identify"]} - responseType="code" - redirectUri="https://manti.vendicated.dev/api/reviewdb/auth" - permissions={0n} - clientId="915703782174752809" - cancelCompletesFlow={false} - callback={async (response: any) => { - try { - const url = new URL(response.location); - url.searchParams.append("clientMod", "vencord"); - const res = await fetch(url, { - headers: new Headers({ Accept: "application/json" }) - }); - const { token, success } = await res.json(); - if (success) { - settings.store.token = token; - showToast("Successfully logged in!"); - callback?.(); - } else if (res.status === 1) { - showToast("An Error occurred while logging in."); - } - } catch (e) { - new Logger("ReviewDB").error("Failed to authorize", e); - } - }} - /> - ); -} - -export function showToast(text: string) { - Toasts.show({ - type: Toasts.Type.MESSAGE, - message: text, - id: Toasts.genId(), - options: { - position: Toasts.Position.BOTTOM - }, - }); -} - export function canDeleteReview(profileId: string, review: Review) { const myId = UserStore.getCurrentUser().id; return ( myId === profileId || review.sender.discordID === profileId - || settings.store.user?.type === UserType.Admin + || Auth.user?.type === UserType.Admin ); } From ac1b67ccbdf0f46fe9d18a0a5ef4145d494b879f Mon Sep 17 00:00:00 2001 From: Vendicated <vendicated@riseup.net> Date: Sat, 7 Oct 2023 23:00:44 +0200 Subject: [PATCH 22/38] Experiments(isStaff): Fix search history not showing --- src/plugins/experiments/index.tsx | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/plugins/experiments/index.tsx b/src/plugins/experiments/index.tsx index d38687f5..cf5f4e6f 100644 --- a/src/plugins/experiments/index.tsx +++ b/src/plugins/experiments/index.tsx @@ -33,12 +33,6 @@ const settings = definePluginSettings({ type: OptionType.BOOLEAN, default: false, restartNeeded: true - }, - forceStagingBanner: { - description: "Whether to force Staging banner under user area.", - type: OptionType.BOOLEAN, - default: false, - restartNeeded: true } }); @@ -83,12 +77,13 @@ export default definePlugin({ } ] }, + // Fix search history being disabled / broken with isStaff { - find: ".Messages.DEV_NOTICE_STAGING", - predicate: () => settings.store.forceStagingBanner, + find: 'get("disable_new_search")', + predicate: () => settings.store.enableIsStaff, replacement: { - match: /"staging"===window\.GLOBAL_ENV\.RELEASE_CHANNEL/, - replace: "true" + match: /(?<=showNewSearch"\);return)\s?!/, + replace: "!1&&!" } }, { From c33d59b45dc2e63ff80ce02287415f8973d90841 Mon Sep 17 00:00:00 2001 From: Vendicated <vendicated@riseup.net> Date: Sun, 8 Oct 2023 03:49:00 +0200 Subject: [PATCH 23/38] serverProfile: fix crash with lurked guilds Co-authored-by: aamiaa <9750071+aamiaa@users.noreply.github.com> --- src/plugins/serverProfile/GuildProfileModal.tsx | 2 +- src/plugins/serverProfile/index.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/serverProfile/GuildProfileModal.tsx b/src/plugins/serverProfile/GuildProfileModal.tsx index 79b38777..2be9b57d 100644 --- a/src/plugins/serverProfile/GuildProfileModal.tsx +++ b/src/plugins/serverProfile/GuildProfileModal.tsx @@ -170,7 +170,7 @@ function ServerInfoTab({ guild }: GuildProps) { const Fields = { "Server Owner": owner ? Owner(guild.id, owner) : "Loading...", "Created At": renderTimestamp(SnowflakeUtils.extractTimestamp(guild.id)), - "Joined At": renderTimestamp(guild.joinedAt.getTime()), + "Joined At": guild.joinedAt ? renderTimestamp(guild.joinedAt.getTime()) : "-", // Not available in lurked guild "Vanity Link": guild.vanityURLCode ? (<a>{`discord.gg/${guild.vanityURLCode}`}</a>) : "-", // Making the anchor href valid would cause Discord to reload "Preferred Locale": guild.preferredLocale || "-", "Verification Level": ["None", "Low", "Medium", "High", "Highest"][guild.verificationLevel] || "?", diff --git a/src/plugins/serverProfile/index.tsx b/src/plugins/serverProfile/index.tsx index c27f8cd5..68f6193c 100644 --- a/src/plugins/serverProfile/index.tsx +++ b/src/plugins/serverProfile/index.tsx @@ -18,7 +18,7 @@ const Patch: NavContextMenuPatchCallback = (children, { guild }: { guild: Guild; group?.push( <Menu.MenuItem id="vc-server-profile" - label="Server Profile" + label="Server Info" action={() => openGuildProfileModal(guild)} /> ); From e4659ed7c3480750df88c182f8bcf6660f98c2c3 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Sat, 7 Oct 2023 23:04:17 -0300 Subject: [PATCH 24/38] Rewrite IgnoreActivities (#1693) --- src/plugins/ignoreActivities/index.tsx | 251 ++++++++++--------------- 1 file changed, 96 insertions(+), 155 deletions(-) diff --git a/src/plugins/ignoreActivities/index.tsx b/src/plugins/ignoreActivities/index.tsx index 6d58eb48..4809e889 100644 --- a/src/plugins/ignoreActivities/index.tsx +++ b/src/plugins/ignoreActivities/index.tsx @@ -1,27 +1,17 @@ /* - * 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 <https://www.gnu.org/licenses/>. -*/ + * Vencord, a Discord client mod + * Copyright (c) 2023 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ import * as DataStore from "@api/DataStore"; +import { definePluginSettings } from "@api/Settings"; +import { getSettingStoreLazy } from "@api/SettingsStore"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import { useForceUpdater } from "@utils/react"; import definePlugin from "@utils/types"; -import { findByPropsLazy, findStoreLazy } from "@webpack"; +import { findStoreLazy } from "@webpack"; import { Tooltip } from "webpack/common"; const enum ActivitiesTypes { @@ -31,203 +21,154 @@ const enum ActivitiesTypes { interface IgnoredActivity { id: string; + name: string; type: ActivitiesTypes; } -const RegisteredGamesClasses = findByPropsLazy("overlayToggleIconOff", "overlayToggleIconOn"); -const TryItOutClasses = findByPropsLazy("tryItOutBadge", "tryItOutBadgeIcon"); -const BaseShapeRoundClasses = findByPropsLazy("baseShapeRound", "baseShapeRoundLeft", "baseShapeRoundRight"); const RunningGameStore = findStoreLazy("RunningGameStore"); +const ShowCurrentGame = getSettingStoreLazy<boolean>("status", "showCurrentGame"); -function ToggleIconOff() { - return ( - <svg - className={RegisteredGamesClasses.overlayToggleIconOff} - height="24" - width="24" - viewBox="0 2.2 32 26" - aria-hidden={true} - role="img" - > - <g - fill="none" - fillRule="evenodd" - > - <path - className={RegisteredGamesClasses.fill} - fill="currentColor" - d="M 16 8 C 7.664063 8 1.25 15.34375 1.25 15.34375 L 0.65625 16 L 1.25 16.65625 C 1.25 16.65625 7.097656 23.324219 14.875 23.9375 C 15.246094 23.984375 15.617188 24 16 24 C 16.382813 24 16.753906 23.984375 17.125 23.9375 C 24.902344 23.324219 30.75 16.65625 30.75 16.65625 L 31.34375 16 L 30.75 15.34375 C 30.75 15.34375 24.335938 8 16 8 Z M 16 10 C 18.203125 10 20.234375 10.601563 22 11.40625 C 22.636719 12.460938 23 13.675781 23 15 C 23 18.613281 20.289063 21.582031 16.78125 21.96875 C 16.761719 21.972656 16.738281 21.964844 16.71875 21.96875 C 16.480469 21.980469 16.242188 22 16 22 C 15.734375 22 15.476563 21.984375 15.21875 21.96875 C 11.710938 21.582031 9 18.613281 9 15 C 9 13.695313 9.351563 12.480469 9.96875 11.4375 L 9.9375 11.4375 C 11.71875 10.617188 13.773438 10 16 10 Z M 16 12 C 14.34375 12 13 13.34375 13 15 C 13 16.65625 14.34375 18 16 18 C 17.65625 18 19 16.65625 19 15 C 19 13.34375 17.65625 12 16 12 Z M 7.25 12.9375 C 7.09375 13.609375 7 14.285156 7 15 C 7 16.753906 7.5 18.394531 8.375 19.78125 C 5.855469 18.324219 4.105469 16.585938 3.53125 16 C 4.011719 15.507813 5.351563 14.203125 7.25 12.9375 Z M 24.75 12.9375 C 26.648438 14.203125 27.988281 15.507813 28.46875 16 C 27.894531 16.585938 26.144531 18.324219 23.625 19.78125 C 24.5 18.394531 25 16.753906 25 15 C 25 14.285156 24.90625 13.601563 24.75 12.9375 Z" - /> - <rect - className={RegisteredGamesClasses.fill} - x="3" - y="26" - width="26" - height="2" - transform="rotate(-45 2 20)" - /> - </g> - </svg> - ); -} - -function ToggleIconOn({ forceWhite }: { forceWhite?: boolean; }) { - return ( - <svg - className={RegisteredGamesClasses.overlayToggleIconOn} - height="24" - width="24" - viewBox="0 2.2 32 26" - > - <path - className={forceWhite ? "" : RegisteredGamesClasses.fill} - fill={forceWhite ? "var(--white-500)" : ""} - d="M 16 8 C 7.664063 8 1.25 15.34375 1.25 15.34375 L 0.65625 16 L 1.25 16.65625 C 1.25 16.65625 7.097656 23.324219 14.875 23.9375 C 15.246094 23.984375 15.617188 24 16 24 C 16.382813 24 16.753906 23.984375 17.125 23.9375 C 24.902344 23.324219 30.75 16.65625 30.75 16.65625 L 31.34375 16 L 30.75 15.34375 C 30.75 15.34375 24.335938 8 16 8 Z M 16 10 C 18.203125 10 20.234375 10.601563 22 11.40625 C 22.636719 12.460938 23 13.675781 23 15 C 23 18.613281 20.289063 21.582031 16.78125 21.96875 C 16.761719 21.972656 16.738281 21.964844 16.71875 21.96875 C 16.480469 21.980469 16.242188 22 16 22 C 15.734375 22 15.476563 21.984375 15.21875 21.96875 C 11.710938 21.582031 9 18.613281 9 15 C 9 13.695313 9.351563 12.480469 9.96875 11.4375 L 9.9375 11.4375 C 11.71875 10.617188 13.773438 10 16 10 Z M 16 12 C 14.34375 12 13 13.34375 13 15 C 13 16.65625 14.34375 18 16 18 C 17.65625 18 19 16.65625 19 15 C 19 13.34375 17.65625 12 16 12 Z M 7.25 12.9375 C 7.09375 13.609375 7 14.285156 7 15 C 7 16.753906 7.5 18.394531 8.375 19.78125 C 5.855469 18.324219 4.105469 16.585938 3.53125 16 C 4.011719 15.507813 5.351563 14.203125 7.25 12.9375 Z M 24.75 12.9375 C 26.648438 14.203125 27.988281 15.507813 28.46875 16 C 27.894531 16.585938 26.144531 18.324219 23.625 19.78125 C 24.5 18.394531 25 16.753906 25 15 C 25 14.285156 24.90625 13.601563 24.75 12.9375 Z" - /> - </svg> - ); -} - -function ToggleActivityComponent({ activity, forceWhite, forceLeftMargin }: { activity: IgnoredActivity; forceWhite?: boolean; forceLeftMargin?: boolean; }) { +function ToggleIcon(activity: IgnoredActivity, tooltipText: string, path: string, fill: string) { const forceUpdate = useForceUpdater(); return ( - <Tooltip text="Toggle activity"> - {({ onMouseLeave, onMouseEnter }) => ( - <div - onMouseLeave={onMouseLeave} - onMouseEnter={onMouseEnter} - className={RegisteredGamesClasses.overlayToggleIcon} - role="button" - aria-label="Toggle activity" - tabIndex={0} - style={forceLeftMargin ? { marginLeft: "2px" } : undefined} + <Tooltip text={tooltipText}> + {tooltipProps => ( + <button + {...tooltipProps} onClick={e => handleActivityToggle(e, activity, forceUpdate)} + style={{ all: "unset", cursor: "pointer", display: "flex", justifyContent: "center", alignItems: "center" }} > - { - ignoredActivitiesCache.has(activity.id) - ? <ToggleIconOff /> - : <ToggleIconOn forceWhite={forceWhite} /> - } - </div> + <svg + width="24" + height="24" + viewBox="0 -960 960 960" + > + <path fill={fill} d={path} /> + </svg> + </button> )} </Tooltip> ); } -function ToggleActivityComponentWithBackground({ activity }: { activity: IgnoredActivity; }) { - return ( - <div - className={`${TryItOutClasses.tryItOutBadge} ${BaseShapeRoundClasses.baseShapeRound}`} - style={{ padding: "0px 2px", height: 28 }} - > - <ToggleActivityComponent activity={activity} forceWhite={true} /> - </div> - ); +const ToggleIconOn = (activity: IgnoredActivity, fill: string) => ToggleIcon(activity, "Disable Activity", "M480-320q75 0 127.5-52.5T660-500q0-75-52.5-127.5T480-680q-75 0-127.5 52.5T300-500q0 75 52.5 127.5T480-320Zm0-72q-45 0-76.5-31.5T372-500q0-45 31.5-76.5T480-608q45 0 76.5 31.5T588-500q0 45-31.5 76.5T480-392Zm0 192q-146 0-266-81.5T40-500q54-137 174-218.5T480-800q146 0 266 81.5T920-500q-54 137-174 218.5T480-200Zm0-300Zm0 220q113 0 207.5-59.5T832-500q-50-101-144.5-160.5T480-720q-113 0-207.5 59.5T128-500q50 101 144.5 160.5T480-280Z", fill); +const ToggleIconOff = (activity: IgnoredActivity, fill: string) => ToggleIcon(activity, "Enable Activity", "m644-428-58-58q9-47-27-88t-93-32l-58-58q17-8 34.5-12t37.5-4q75 0 127.5 52.5T660-500q0 20-4 37.5T644-428Zm128 126-58-56q38-29 67.5-63.5T832-500q-50-101-143.5-160.5T480-720q-29 0-57 4t-55 12l-62-62q41-17 84-25.5t90-8.5q151 0 269 83.5T920-500q-23 59-60.5 109.5T772-302Zm20 246L624-222q-35 11-70.5 16.5T480-200q-151 0-269-83.5T40-500q21-53 53-98.5t73-81.5L56-792l56-56 736 736-56 56ZM222-624q-29 26-53 57t-41 67q50 101 143.5 160.5T480-280q20 0 39-2.5t39-5.5l-36-38q-11 3-21 4.5t-21 1.5q-75 0-127.5-52.5T300-500q0-11 1.5-21t4.5-21l-84-82Zm319 93Zm-151 75Z", fill); + +function ToggleActivityComponent(activity: IgnoredActivity, isPlaying = false) { + if (getIgnoredActivities().some(act => act.id === activity.id)) return ToggleIconOff(activity, "var(--status-danger)"); + return ToggleIconOn(activity, isPlaying ? "var(--green-300)" : "var(--primary-400)"); } -function handleActivityToggle(e: React.MouseEvent<HTMLDivElement, MouseEvent>, activity: IgnoredActivity, forceUpdateComponent: () => void) { +function handleActivityToggle(e: React.MouseEvent<HTMLButtonElement, MouseEvent>, activity: IgnoredActivity, forceUpdateButton: () => void) { e.stopPropagation(); - if (ignoredActivitiesCache.has(activity.id)) ignoredActivitiesCache.delete(activity.id); - else ignoredActivitiesCache.set(activity.id, activity); - forceUpdateComponent(); - saveCacheToDatastore(); + + const ignoredActivityIndex = getIgnoredActivities().findIndex(act => act.id === activity.id); + if (ignoredActivityIndex === -1) settings.store.ignoredActivities = getIgnoredActivities().concat(activity); + else settings.store.ignoredActivities = getIgnoredActivities().filter((_, index) => index !== ignoredActivityIndex); + + // Trigger activities recalculation + ShowCurrentGame?.updateSetting(old => old); + forceUpdateButton(); } -async function saveCacheToDatastore() { - await DataStore.set("IgnoreActivities_ignoredActivities", ignoredActivitiesCache); -} +const settings = definePluginSettings({}).withPrivateSettings<{ + ignoredActivities: IgnoredActivity[]; +}>(); -let ignoredActivitiesCache = new Map<IgnoredActivity["id"], IgnoredActivity>(); +function getIgnoredActivities() { + return settings.store.ignoredActivities ??= []; +} export default definePlugin({ name: "IgnoreActivities", authors: [Devs.Nuckyz], - description: "Ignore certain activities (like games and actual activities) from showing up on your status. You can configure which ones are ignored from the Registered Games and Activities tabs.", + description: "Ignore activities from showing up on your status ONLY. You can configure which ones are ignored from the Registered Games and Activities tabs.", + + dependencies: ["SettingsStoreAPI"], + settings, + patches: [ + { + find: '.displayName="LocalActivityStore"', + replacement: [ + { + match: /LISTENING.+?\)\);(?<=(\i)\.push.+?)/, + replace: (m, activities) => `${m}${activities}=${activities}.filter($self.isActivityNotIgnored);` + } + ] + }, { find: ".Messages.SETTINGS_GAMES_TOGGLE_OVERLAY", replacement: { - match: /!(\i)(\)return null;var \i=(\i)\.overlay.+?children:)(\[.{0,70}overlayStatusText.+?\])(?=}\)}\(\))/, - replace: (_, platformCheck, restWithoutPlatformCheck, props, children) => "false" - + `${restWithoutPlatformCheck}` - + `(${platformCheck}?${children}:[])` - + `.concat(Vencord.Plugins.plugins.IgnoreActivities.renderToggleGameActivityButton(${props}))` + match: /\(\)\.removeGame.+?null(?<=(\i)\?\i=\i\.\i\.Messages\.SETTINGS_GAMES_NOW_PLAYING_STATE.+?=(\i)\.overlay.+?)/, + replace: (m, nowPlaying, props) => `${m},$self.renderToggleGameActivityButton(${props},${nowPlaying})` } }, { find: ".overlayBadge", replacement: [ { - match: /(?<=\(\)\.badgeContainer,children:).{0,50}?name:(\i)\.name.+?null/, - replace: (m, props) => `[${m},$self.renderToggleActivityButton(${props})]` + match: /(?<=\(\)\.activityTitleText.+?children:(\i)\.name.*?}\),)/, + replace: (_, props) => `$self.renderToggleActivityButton(${props}),` }, { - match: /(?<=\(\)\.badgeContainer,children:).{0,50}?name:(\i\.application)\.name.+?null/, - replace: (m, props) => `${m},$self.renderToggleActivityButton(${props})` + match: /(?<=\(\)\.activityCardDetails.+?children:(\i\.application)\.name.*?}\),)/, + replace: (_, props) => `$self.renderToggleActivityButton(${props}),` } ] - }, - { - find: '.displayName="LocalActivityStore"', - replacement: { - match: /LISTENING.+?\)\);(?<=(\i)\.push.+?)/, - replace: (m, activities) => `${m}${activities}=${activities}.filter($self.isActivityNotIgnored);` - } } ], async start() { - const ignoredActivitiesData = await DataStore.get<string[] | Map<IgnoredActivity["id"], IgnoredActivity>>("IgnoreActivities_ignoredActivities") ?? new Map<IgnoredActivity["id"], IgnoredActivity>(); - /** Migrate old data */ - if (Array.isArray(ignoredActivitiesData)) { - for (const id of ignoredActivitiesData) { - ignoredActivitiesCache.set(id, { id, type: ActivitiesTypes.Game }); - } + const oldIgnoredActivitiesData = await DataStore.get<Map<IgnoredActivity["id"], IgnoredActivity>>("IgnoreActivities_ignoredActivities"); - await saveCacheToDatastore(); - } else ignoredActivitiesCache = ignoredActivitiesData; + if (oldIgnoredActivitiesData != null) { + settings.store.ignoredActivities = Array.from(oldIgnoredActivitiesData.values()) + .map(activity => ({ ...activity, name: "Unknown Name" })); - if (ignoredActivitiesCache.size !== 0) { - const gamesSeen: { id?: string; exePath: string; }[] = RunningGameStore.getGamesSeen(); + DataStore.del("IgnoreActivities_ignoredActivities"); + } - for (const ignoredActivity of ignoredActivitiesCache.values()) { + if (getIgnoredActivities().length !== 0) { + const gamesSeen = RunningGameStore.getGamesSeen() as { id?: string; exePath: string; }[]; + + for (const [index, ignoredActivity] of getIgnoredActivities().entries()) { if (ignoredActivity.type !== ActivitiesTypes.Game) continue; if (!gamesSeen.some(game => game.id === ignoredActivity.id || game.exePath === ignoredActivity.id)) { - /** Custom added game which no longer exists */ - ignoredActivitiesCache.delete(ignoredActivity.id); + getIgnoredActivities().splice(index, 1); } } - - await saveCacheToDatastore(); } }, - renderToggleGameActivityButton(props: { id?: string; exePath: string; }) { - return ( - <ErrorBoundary noop> - <ToggleActivityComponent activity={{ id: props.id ?? props.exePath, type: ActivitiesTypes.Game }} forceLeftMargin={true} /> - </ErrorBoundary> - ); - }, - - renderToggleActivityButton(props: { id: string; }) { - return ( - <ErrorBoundary noop> - <ToggleActivityComponentWithBackground activity={{ id: props.id, type: ActivitiesTypes.Embedded }} /> - </ErrorBoundary> - ); - }, - isActivityNotIgnored(props: { type: number; application_id?: string; name?: string; }) { - if (props.type === 0) { - if (props.application_id !== undefined) return !ignoredActivitiesCache.has(props.application_id); + if (props.type === 0 || props.type === 3) { + if (props.application_id != null) return !getIgnoredActivities().some(activity => activity.id === props.application_id); else { const exePath = RunningGameStore.getRunningGames().find(game => game.name === props.name)?.exePath; - if (exePath) return !ignoredActivitiesCache.has(exePath); + if (exePath) return !getIgnoredActivities().some(activity => activity.id === exePath); } } return true; + }, + + renderToggleGameActivityButton(props: { id?: string; name: string, exePath: string; }, nowPlaying: boolean) { + return ( + <ErrorBoundary noop> + <div style={{ marginLeft: 12, zIndex: 0 }}> + {ToggleActivityComponent({ id: props.id ?? props.exePath, name: props.name, type: ActivitiesTypes.Game }, nowPlaying)} + </div> + </ErrorBoundary> + ); + }, + + renderToggleActivityButton(props: { id: string; name: string; }) { + return ( + <ErrorBoundary noop> + {ToggleActivityComponent({ id: props.id, name: props.name, type: ActivitiesTypes.Embedded })} + </ErrorBoundary> + ); } }); From 34ac71870529f6fda5abcd98af664012b4f44873 Mon Sep 17 00:00:00 2001 From: Nico <nico@d3sox.me> Date: Sun, 8 Oct 2023 04:20:36 +0200 Subject: [PATCH 25/38] fix(forceOwnerCrown): update broken patch (#1777) --- src/plugins/forceOwnerCrown/index.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/forceOwnerCrown/index.ts b/src/plugins/forceOwnerCrown/index.ts index 3122410f..5fa63f00 100644 --- a/src/plugins/forceOwnerCrown/index.ts +++ b/src/plugins/forceOwnerCrown/index.ts @@ -27,12 +27,12 @@ export default definePlugin({ patches: [ { // This is the logic where it decides whether to render the owner crown or not - find: ".renderOwner=", + find: ".MULTIPLE_AVATAR", replacement: { - match: /isOwner;return null!=(\w+)?&&/g, - replace: "isOwner;if($self.isGuildOwner(this.props)){$1=true;}return null!=$1&&" + match: /(\i)=(\i)\.isOwner,/, + replace: "$1=$self.isGuildOwner($2)," } - }, + } ], isGuildOwner(props) { // Check if channel is a Group DM, if so return false From 377cf600550da9c1b49924bd996fc86be9011048 Mon Sep 17 00:00:00 2001 From: V <vendicated@riseup.net> Date: Mon, 9 Oct 2023 03:01:35 +0200 Subject: [PATCH 26/38] fix global settings listeners --- src/api/Settings.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/api/Settings.ts b/src/api/Settings.ts index c380f631..368f88f7 100644 --- a/src/api/Settings.ts +++ b/src/api/Settings.ts @@ -237,7 +237,8 @@ type ResolvePropDeep<T, P> = P extends "" ? T : export function addSettingsListener<Path extends keyof Settings>(path: Path, onUpdate: (newValue: Settings[Path], path: Path) => void): void; export function addSettingsListener<Path extends string>(path: Path, onUpdate: (newValue: Path extends "" ? any : ResolvePropDeep<Settings, Path>, path: Path extends "" ? string : Path) => void): void; export function addSettingsListener(path: string, onUpdate: (newValue: any, path: string) => void) { - ((onUpdate as SubscriptionCallback)._paths ??= []).push(path); + if (path) + ((onUpdate as SubscriptionCallback)._paths ??= []).push(path); subscriptions.add(onUpdate); } From 390987e4a9d58c4c0eb9d4f6b4101ecf1203ccba Mon Sep 17 00:00:00 2001 From: V <vendicated@riseup.net> Date: Mon, 9 Oct 2023 03:09:56 +0200 Subject: [PATCH 27/38] Remove ReviewDB abuse / harassment has gotten pretty bad. i proposed the ability to allow users to delete comments from their own profile, but there seems to be no interest among the reviewdb owners. i don't want vencord to harbour harassment, so i'm removing the plugin for now --- src/plugins/reviewDB/auth.tsx | 78 ------- .../reviewDB/components/MessageButton.tsx | 61 ------ .../reviewDB/components/ReviewBadge.tsx | 46 ---- .../reviewDB/components/ReviewComponent.tsx | 147 ------------- .../reviewDB/components/ReviewModal.tsx | 105 --------- .../reviewDB/components/ReviewsView.tsx | 200 ------------------ src/plugins/reviewDB/entities.ts | 96 --------- src/plugins/reviewDB/index.tsx | 157 -------------- src/plugins/reviewDB/reviewDbApi.ts | 153 -------------- src/plugins/reviewDB/settings.tsx | 85 -------- src/plugins/reviewDB/style.css | 76 ------- src/plugins/reviewDB/utils.tsx | 34 --- 12 files changed, 1238 deletions(-) delete mode 100644 src/plugins/reviewDB/auth.tsx delete mode 100644 src/plugins/reviewDB/components/MessageButton.tsx delete mode 100644 src/plugins/reviewDB/components/ReviewBadge.tsx delete mode 100644 src/plugins/reviewDB/components/ReviewComponent.tsx delete mode 100644 src/plugins/reviewDB/components/ReviewModal.tsx delete mode 100644 src/plugins/reviewDB/components/ReviewsView.tsx delete mode 100644 src/plugins/reviewDB/entities.ts delete mode 100644 src/plugins/reviewDB/index.tsx delete mode 100644 src/plugins/reviewDB/reviewDbApi.ts delete mode 100644 src/plugins/reviewDB/settings.tsx delete mode 100644 src/plugins/reviewDB/style.css delete mode 100644 src/plugins/reviewDB/utils.tsx diff --git a/src/plugins/reviewDB/auth.tsx b/src/plugins/reviewDB/auth.tsx deleted file mode 100644 index 1d95e47d..00000000 --- a/src/plugins/reviewDB/auth.tsx +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2023 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -import { DataStore } from "@api/index"; -import { Logger } from "@utils/Logger"; -import { openModal } from "@utils/modal"; -import { findByPropsLazy } from "@webpack"; -import { showToast, Toasts, UserStore } from "@webpack/common"; - -import { ReviewDBAuth } from "./entities"; - -const DATA_STORE_KEY = "rdb-auth"; - -const OAuth = findByPropsLazy("OAuth2AuthorizeModal"); - -export let Auth: ReviewDBAuth = {}; - -export async function initAuth() { - Auth = await getAuth() ?? {}; -} - -export async function getAuth(): Promise<ReviewDBAuth | undefined> { - const auth = await DataStore.get(DATA_STORE_KEY); - return auth?.[UserStore.getCurrentUser()?.id]; -} - -export async function getToken() { - const auth = await getAuth(); - return auth?.token; -} - -export async function updateAuth(newAuth: ReviewDBAuth) { - return DataStore.update(DATA_STORE_KEY, auth => { - auth ??= {}; - Auth = auth[UserStore.getCurrentUser().id] ??= {}; - - if (newAuth.token) Auth.token = newAuth.token; - if (newAuth.user) Auth.user = newAuth.user; - - return auth; - }); -} - -export function authorize(callback?: any) { - openModal(props => - <OAuth.OAuth2AuthorizeModal - {...props} - scopes={["identify"]} - responseType="code" - redirectUri="https://manti.vendicated.dev/api/reviewdb/auth" - permissions={0n} - clientId="915703782174752809" - cancelCompletesFlow={false} - callback={async (response: any) => { - try { - const url = new URL(response.location); - url.searchParams.append("clientMod", "vencord"); - const res = await fetch(url, { - headers: new Headers({ Accept: "application/json" }) - }); - const { token, success } = await res.json(); - if (success) { - updateAuth({ token }); - showToast("Successfully logged in!"); - callback?.(); - } else if (res.status === 1) { - showToast("An Error occurred while logging in.", Toasts.Type.FAILURE); - } - } catch (e) { - new Logger("ReviewDB").error("Failed to authorize", e); - } - }} - /> - ); -} diff --git a/src/plugins/reviewDB/components/MessageButton.tsx b/src/plugins/reviewDB/components/MessageButton.tsx deleted file mode 100644 index 965fd1c1..00000000 --- a/src/plugins/reviewDB/components/MessageButton.tsx +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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 <https://www.gnu.org/licenses/>. -*/ - -import { DeleteIcon } from "@components/Icons"; -import { classes } from "@utils/misc"; -import { findByPropsLazy } from "@webpack"; -import { Tooltip } from "@webpack/common"; - -const iconClasses = findByPropsLazy("button", "wrapper", "disabled", "separator"); - -export function DeleteButton({ onClick }: { onClick(): void; }) { - return ( - <Tooltip text="Delete Review"> - {props => ( - <div - {...props} - className={classes(iconClasses.button, iconClasses.dangerous)} - onClick={onClick} - > - <DeleteIcon width="20" height="20" /> - </div> - )} - </Tooltip> - ); -} - -export function ReportButton({ onClick }: { onClick(): void; }) { - return ( - <Tooltip text="Report Review"> - {props => ( - <div - {...props} - className={iconClasses.button} - onClick={onClick} - > - <svg width="20" height="20" viewBox="0 0 24 24"> - <path - fill="currentColor" - d="M20,6.002H14V3.002C14,2.45 13.553,2.002 13,2.002H4C3.447,2.002 3,2.45 3,3.002V22.002H5V14.002H10.586L8.293,16.295C8.007,16.581 7.922,17.011 8.076,17.385C8.23,17.759 8.596,18.002 9,18.002H20C20.553,18.002 21,17.554 21,17.002V7.002C21,6.45 20.553,6.002 20,6.002Z" - /> - </svg> - </div> - )} - </Tooltip> - ); -} diff --git a/src/plugins/reviewDB/components/ReviewBadge.tsx b/src/plugins/reviewDB/components/ReviewBadge.tsx deleted file mode 100644 index 3c02c354..00000000 --- a/src/plugins/reviewDB/components/ReviewBadge.tsx +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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 <https://www.gnu.org/licenses/>. -*/ - -import { MaskedLinkStore, Tooltip } from "@webpack/common"; - -import { Badge } from "../entities"; -import { cl } from "../utils"; - -export default function ReviewBadge(badge: Badge) { - return ( - <Tooltip - text={badge.name}> - {({ onMouseEnter, onMouseLeave }) => ( - <img - className={cl("badge")} - width="22px" - height="22px" - onMouseEnter={onMouseEnter} - onMouseLeave={onMouseLeave} - src={badge.icon} - alt={badge.description} - onClick={() => - MaskedLinkStore.openUntrustedLink({ - href: badge.redirectURL, - }) - } - /> - )} - </Tooltip> - ); -} diff --git a/src/plugins/reviewDB/components/ReviewComponent.tsx b/src/plugins/reviewDB/components/ReviewComponent.tsx deleted file mode 100644 index 18659179..00000000 --- a/src/plugins/reviewDB/components/ReviewComponent.tsx +++ /dev/null @@ -1,147 +0,0 @@ -/* - * 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 <https://www.gnu.org/licenses/>. -*/ - -import { openUserProfile } from "@utils/discord"; -import { classes } from "@utils/misc"; -import { LazyComponent } from "@utils/react"; -import { filters, findBulk } from "@webpack"; -import { Alerts, moment, Parser, showToast, Timestamp } from "@webpack/common"; - -import { Review, ReviewType } from "../entities"; -import { deleteReview, reportReview } from "../reviewDbApi"; -import { settings } from "../settings"; -import { canDeleteReview, cl } from "../utils"; -import { DeleteButton, ReportButton } from "./MessageButton"; -import ReviewBadge from "./ReviewBadge"; - -export default LazyComponent(() => { - // this is terrible, blame mantika - const p = filters.byProps; - const [ - { cozyMessage, buttons, message, buttonsInner, groupStart }, - { container, isHeader }, - { avatar, clickable, username, wrapper, cozy }, - buttonClasses, - botTag - ] = findBulk( - p("cozyMessage"), - p("container", "isHeader"), - p("avatar", "zalgo"), - p("button", "wrapper", "selected"), - p("botTag", "botTagRegular") - ); - - const dateFormat = new Intl.DateTimeFormat(); - - return function ReviewComponent({ review, refetch, profileId }: { review: Review; refetch(): void; profileId: string; }) { - function openModal() { - openUserProfile(review.sender.discordID); - } - - function delReview() { - Alerts.show({ - title: "Are you sure?", - body: "Do you really want to delete this review?", - confirmText: "Delete", - cancelText: "Nevermind", - onConfirm: () => { - deleteReview(review.id).then(res => { - if (res.success) { - refetch(); - } - showToast(res.message); - }); - } - }); - } - - function reportRev() { - Alerts.show({ - title: "Are you sure?", - body: "Do you really you want to report this review?", - confirmText: "Report", - cancelText: "Nevermind", - // confirmColor: "red", this just adds a class name and breaks the submit button guh - onConfirm: () => reportReview(review.id) - }); - } - - return ( - <div className={classes(cozyMessage, wrapper, message, groupStart, cozy, cl("review"))} style={ - { - marginLeft: "0px", - paddingLeft: "52px", // wth is this - paddingRight: "16px" - } - }> - - <img - className={classes(avatar, clickable)} - onClick={openModal} - src={review.sender.profilePhoto || "/assets/1f0bfc0865d324c2587920a7d80c609b.png?size=128"} - style={{ left: "0px", zIndex: 0 }} - /> - <div style={{ display: "inline-flex", justifyContent: "center", alignItems: "center" }}> - <span - className={classes(clickable, username)} - style={{ color: "var(--channels-default)", fontSize: "14px" }} - onClick={() => openModal()} - > - {review.sender.username} - </span> - - {review.type === ReviewType.System && ( - <span - className={classes(botTag.botTagVerified, botTag.botTagRegular, botTag.botTag, botTag.px, botTag.rem)} - style={{ marginLeft: "4px" }}> - <span className={botTag.botText}> - System - </span> - </span> - )} - </div> - {review.sender.badges.map(badge => <ReviewBadge {...badge} />)} - - { - !settings.store.hideTimestamps && review.type !== ReviewType.System && ( - <Timestamp timestamp={moment(review.timestamp * 1000)} > - {dateFormat.format(review.timestamp * 1000)} - </Timestamp>) - } - - <div className={cl("review-comment")}> - {Parser.parseGuildEventDescription(review.comment)} - </div> - - {review.id !== 0 && ( - <div className={classes(container, isHeader, buttons)} style={{ - padding: "0px", - }}> - <div className={classes(buttonClasses.wrapper, buttonsInner)} > - <ReportButton onClick={reportRev} /> - - {canDeleteReview(profileId, review) && ( - <DeleteButton onClick={delReview} /> - )} - </div> - </div> - )} - </div> - ); - }; -}); diff --git a/src/plugins/reviewDB/components/ReviewModal.tsx b/src/plugins/reviewDB/components/ReviewModal.tsx deleted file mode 100644 index 9669a2b3..00000000 --- a/src/plugins/reviewDB/components/ReviewModal.tsx +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Vencord, a modification for Discord's desktop app - * Copyright (c) 2023 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 <https://www.gnu.org/licenses/>. -*/ - -import ErrorBoundary from "@components/ErrorBoundary"; -import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalRoot, ModalSize, openModal } from "@utils/modal"; -import { useForceUpdater } from "@utils/react"; -import { Paginator, Text, useRef, useState } from "@webpack/common"; - -import { Auth } from "../auth"; -import { Response, REVIEWS_PER_PAGE } from "../reviewDbApi"; -import { cl } from "../utils"; -import ReviewComponent from "./ReviewComponent"; -import ReviewsView, { ReviewsInputComponent } from "./ReviewsView"; - -function Modal({ modalProps, discordId, name }: { modalProps: any; discordId: string; name: string; }) { - const [data, setData] = useState<Response>(); - const [signal, refetch] = useForceUpdater(true); - const [page, setPage] = useState(1); - - const ref = useRef<HTMLDivElement>(null); - - const reviewCount = data?.reviewCount; - const ownReview = data?.reviews.find(r => r.sender.discordID === Auth.user?.discordID); - - return ( - <ErrorBoundary> - <ModalRoot {...modalProps} size={ModalSize.MEDIUM}> - <ModalHeader> - <Text variant="heading-lg/semibold" className={cl("modal-header")}> - {name}'s Reviews - {!!reviewCount && <span> ({reviewCount} Reviews)</span>} - </Text> - <ModalCloseButton onClick={modalProps.onClose} /> - </ModalHeader> - - <ModalContent scrollerRef={ref}> - <div className={cl("modal-reviews")}> - <ReviewsView - discordId={discordId} - name={name} - page={page} - refetchSignal={signal} - onFetchReviews={setData} - scrollToTop={() => ref.current?.scrollTo({ top: 0, behavior: "smooth" })} - hideOwnReview - /> - </div> - </ModalContent> - - <ModalFooter className={cl("modal-footer")}> - <div> - {ownReview && ( - <ReviewComponent - refetch={refetch} - review={ownReview} - profileId={discordId} - /> - )} - <ReviewsInputComponent - isAuthor={ownReview != null} - discordId={discordId} - name={name} - refetch={refetch} - /> - - {!!reviewCount && ( - <Paginator - currentPage={page} - maxVisiblePages={5} - pageSize={REVIEWS_PER_PAGE} - totalCount={reviewCount} - onPageChange={setPage} - /> - )} - </div> - </ModalFooter> - </ModalRoot> - </ErrorBoundary> - ); -} - -export function openReviewsModal(discordId: string, name: string) { - openModal(props => ( - <Modal - modalProps={props} - discordId={discordId} - name={name} - /> - )); -} diff --git a/src/plugins/reviewDB/components/ReviewsView.tsx b/src/plugins/reviewDB/components/ReviewsView.tsx deleted file mode 100644 index a87598be..00000000 --- a/src/plugins/reviewDB/components/ReviewsView.tsx +++ /dev/null @@ -1,200 +0,0 @@ -/* - * 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 <https://www.gnu.org/licenses/>. -*/ - -import { LazyComponent, useAwaiter, useForceUpdater } from "@utils/react"; -import { find, findByPropsLazy } from "@webpack"; -import { Forms, React, RelationshipStore, showToast, useRef, UserStore } from "@webpack/common"; - -import { Auth, authorize } from "../auth"; -import { Review } from "../entities"; -import { addReview, getReviews, Response, REVIEWS_PER_PAGE } from "../reviewDbApi"; -import { settings } from "../settings"; -import { cl } from "../utils"; -import ReviewComponent from "./ReviewComponent"; - - -const Editor = findByPropsLazy("start", "end", "addMark"); -const Transform = findByPropsLazy("unwrapNodes"); -const InputTypes = findByPropsLazy("VOICE_CHANNEL_STATUS", "SIDEBAR"); - -const InputComponent = LazyComponent(() => find(m => m?.type?.render?.toString().includes("CHANNEL_TEXT_AREA).AnalyticsLocationProvider"))); - -interface UserProps { - discordId: string; - name: string; -} - -interface Props extends UserProps { - onFetchReviews(data: Response): void; - refetchSignal?: unknown; - showInput?: boolean; - page?: number; - scrollToTop?(): void; - hideOwnReview?: boolean; -} - -export default function ReviewsView({ - discordId, - name, - onFetchReviews, - refetchSignal, - scrollToTop, - page = 1, - showInput = false, - hideOwnReview = false, -}: Props) { - const [signal, refetch] = useForceUpdater(true); - - const [reviewData] = useAwaiter(() => getReviews(discordId, (page - 1) * REVIEWS_PER_PAGE), { - fallbackValue: null, - deps: [refetchSignal, signal, page], - onSuccess: data => { - if (settings.store.hideBlockedUsers) - data!.reviews = data!.reviews?.filter(r => !RelationshipStore.isBlocked(r.sender.discordID)); - - scrollToTop?.(); - onFetchReviews(data!); - } - }); - - if (!reviewData) return null; - - return ( - <> - <ReviewList - refetch={refetch} - reviews={reviewData!.reviews} - hideOwnReview={hideOwnReview} - profileId={discordId} - /> - - {showInput && ( - <ReviewsInputComponent - name={name} - discordId={discordId} - refetch={refetch} - isAuthor={reviewData!.reviews?.some(r => r.sender.discordID === UserStore.getCurrentUser().id)} - /> - )} - </> - ); -} - -function ReviewList({ refetch, reviews, hideOwnReview, profileId }: { refetch(): void; reviews: Review[]; hideOwnReview: boolean; profileId: string; }) { - const myId = UserStore.getCurrentUser().id; - - return ( - <div className={cl("view")}> - {reviews?.map(review => - (review.sender.discordID !== myId || !hideOwnReview) && - <ReviewComponent - key={review.id} - review={review} - refetch={refetch} - profileId={profileId} - /> - )} - - {reviews?.length === 0 && ( - <Forms.FormText className={cl("placeholder")}> - Looks like nobody reviewed this user yet. You could be the first! - </Forms.FormText> - )} - </div> - ); -} - - -export function ReviewsInputComponent({ discordId, isAuthor, refetch, name }: { discordId: string, name: string; isAuthor: boolean; refetch(): void; }) { - const { token } = Auth; - const editorRef = useRef<any>(null); - const inputType = InputTypes.FORM; - inputType.disableAutoFocus = true; - - 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 ( - <> - <div onClick={() => { - if (!token) { - showToast("Opening authorization window..."); - authorize(); - } - }}> - <InputComponent - className={cl("input")} - channel={channel} - placeholder={ - !token - ? "You need to authorize to review users!" - : isAuthor - ? `Update review for @${name}` - : `Review @${name}` - } - type={inputType} - disableThemedBackground={true} - setEditorRef={ref => editorRef.current = ref} - textValue="" - onSubmit={ - async res => { - const response = await addReview({ - userid: discordId, - comment: res.value, - }); - - if (response?.success) { - refetch(); - - const slateEditor = editorRef.current.ref.current.getSlateEditor(); - - // clear editor - Transform.delete(slateEditor, { - at: { - anchor: Editor.start(slateEditor, []), - focus: Editor.end(slateEditor, []), - } - }); - } else if (response?.message) { - showToast(response.message); - } - - // even tho we need to return this, it doesnt do anything - return { - shouldClear: false, - shouldRefocus: true, - }; - } - } - /> - </div> - - </> - ); -} diff --git a/src/plugins/reviewDB/entities.ts b/src/plugins/reviewDB/entities.ts deleted file mode 100644 index 0a77fef0..00000000 --- a/src/plugins/reviewDB/entities.ts +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Vencord, a modification for Discord's desktop app - * Copyright (c) 2023 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 <https://www.gnu.org/licenses/>. -*/ - -export const enum UserType { - Banned = -1, - Normal = 0, - Admin = 1 -} - -export const enum ReviewType { - User = 0, - Server = 1, - Support = 2, - System = 3 -} - -export const enum NotificationType { - Info = 0, - Ban = 1, - Unban = 2, - Warning = 3 -} - -export interface ReviewDBAuth { - token?: string; - user?: ReviewDBUser; -} - -export interface Badge { - name: string; - description: string; - icon: string; - redirectURL: string; - type: number; -} - -export interface BanInfo { - id: string; - discordID: string; - reviewID: number; - reviewContent: string; - banEndDate: number; -} - -export interface Notification { - id: number; - title: string; - content: string; - type: NotificationType; -} - -export interface ReviewDBUser { - ID: number; - discordID: string; - username: string; - profilePhoto: string; - clientMod: string; - warningCount: number; - badges: any[]; - banInfo: BanInfo | null; - notification: Notification | null; - lastReviewID: number; - type: UserType; -} - -export interface ReviewAuthor { - id: number, - discordID: string, - username: string, - profilePhoto: string, - badges: Badge[]; -} - -export interface Review { - comment: string, - id: number, - star: number, - sender: ReviewAuthor, - timestamp: number; - type?: ReviewType; -} diff --git a/src/plugins/reviewDB/index.tsx b/src/plugins/reviewDB/index.tsx deleted file mode 100644 index b9350ba4..00000000 --- a/src/plugins/reviewDB/index.tsx +++ /dev/null @@ -1,157 +0,0 @@ -/* - * 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 <https://www.gnu.org/licenses/>. -*/ - -import "./style.css"; - -import { addContextMenuPatch, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu"; -import ErrorBoundary from "@components/ErrorBoundary"; -import ExpandableHeader from "@components/ExpandableHeader"; -import { OpenExternalIcon } from "@components/Icons"; -import { Devs } from "@utils/constants"; -import { Logger } from "@utils/Logger"; -import definePlugin from "@utils/types"; -import { Alerts, Menu, Parser, showToast, useState } from "@webpack/common"; -import { Guild, User } from "discord-types/general"; - -import { Auth, initAuth, updateAuth } from "./auth"; -import { openReviewsModal } from "./components/ReviewModal"; -import ReviewsView from "./components/ReviewsView"; -import { NotificationType } from "./entities"; -import { getCurrentUserInfo, readNotification } from "./reviewDbApi"; -import { settings } from "./settings"; - -const guildPopoutPatch: NavContextMenuPatchCallback = (children, props: { guild: Guild, onClose(): void; }) => () => { - children.push( - <Menu.MenuItem - label="View Reviews" - id="vc-rdb-server-reviews" - icon={OpenExternalIcon} - action={() => openReviewsModal(props.guild.id, props.guild.name)} - /> - ); -}; - -export default definePlugin({ - name: "ReviewDB", - description: "Review other users (Adds a new settings to profiles)", - authors: [Devs.mantikafasi, Devs.Ven], - - settings, - - patches: [ - { - find: "disableBorderColor:!0", - replacement: { - match: /\(.{0,10}\{user:(.),setNote:.,canDM:.,.+?\}\)/, - replace: "$&,$self.getReviewsComponent($1)" - } - } - ], - - flux: { - CONNECTION_OPEN: initAuth, - }, - - async start() { - addContextMenuPatch("guild-header-popout", guildPopoutPatch); - - const s = settings.store; - const { lastReviewId, notifyReviews } = s; - - const legacy = s as any as { token?: string; }; - if (legacy.token) { - await updateAuth({ token: legacy.token }); - legacy.token = undefined; - new Logger("ReviewDB").info("Migrated legacy settings"); - } - - await initAuth(); - - setTimeout(async () => { - if (!Auth.token) return; - - const user = await getCurrentUserInfo(Auth.token); - updateAuth({ user }); - - if (notifyReviews) { - if (lastReviewId && lastReviewId < user.lastReviewID) { - s.lastReviewId = user.lastReviewID; - if (user.lastReviewID !== 0) - showToast("You have new reviews on your profile!"); - } - } - - if (user.notification) { - const props = user.notification.type === NotificationType.Ban ? { - cancelText: "Appeal", - confirmText: "Ok", - onCancel: async () => - VencordNative.native.openExternal( - "https://reviewdb.mantikafasi.dev/api/redirect?" - + new URLSearchParams({ - token: Auth.token!, - page: "dashboard/appeal" - }) - ) - } : {}; - - Alerts.show({ - title: user.notification.title, - body: ( - Parser.parse( - user.notification.content, - false - ) - ), - ...props - }); - - readNotification(user.notification.id); - } - }, 4000); - }, - - stop() { - removeContextMenuPatch("guild-header-popout", guildPopoutPatch); - }, - - getReviewsComponent: ErrorBoundary.wrap((user: User) => { - const [reviewCount, setReviewCount] = useState<number>(); - - return ( - <ExpandableHeader - headerText="User Reviews" - onMoreClick={() => openReviewsModal(user.id, user.username)} - moreTooltipText={ - reviewCount && reviewCount > 50 - ? `View all ${reviewCount} reviews` - : "Open Review Modal" - } - onDropDownClick={state => settings.store.reviewsDropdownState = !state} - defaultState={settings.store.reviewsDropdownState} - > - <ReviewsView - discordId={user.id} - name={user.username} - onFetchReviews={r => setReviewCount(r.reviewCount)} - showInput - /> - </ExpandableHeader> - ); - }, { message: "Failed to render Reviews" }) -}); diff --git a/src/plugins/reviewDB/reviewDbApi.ts b/src/plugins/reviewDB/reviewDbApi.ts deleted file mode 100644 index add16dd1..00000000 --- a/src/plugins/reviewDB/reviewDbApi.ts +++ /dev/null @@ -1,153 +0,0 @@ -/* - * 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 <https://www.gnu.org/licenses/>. -*/ - -import { showToast, Toasts } from "@webpack/common"; - -import { authorize, getToken } from "./auth"; -import { Review, ReviewDBUser } from "./entities"; -import { settings } from "./settings"; - -const API_URL = "https://manti.vendicated.dev"; - -export const REVIEWS_PER_PAGE = 50; - -export interface Response { - success: boolean, - message: string; - reviews: Review[]; - updated: boolean; - hasNextPage: boolean; - reviewCount: number; -} - -const WarningFlag = 0b00000010; - -export async function getReviews(id: string, offset = 0): Promise<Response> { - let flags = 0; - if (!settings.store.showWarning) flags |= WarningFlag; - - const params = new URLSearchParams({ - flags: String(flags), - offset: String(offset) - }); - const req = await fetch(`${API_URL}/api/reviewdb/users/${id}/reviews?${params}`); - - const res = (req.status === 200) - ? await req.json() as Response - : { - success: false, - message: "An Error occured while fetching reviews. Please try again later.", - reviews: [], - updated: false, - hasNextPage: false, - reviewCount: 0 - }; - - if (!res.success) { - showToast(res.message, Toasts.Type.FAILURE); - return { - ...res, - reviews: [ - { - id: 0, - comment: "An Error occured while fetching reviews. Please try again later.", - star: 0, - timestamp: 0, - sender: { - id: 0, - username: "Error", - profilePhoto: "https://cdn.discordapp.com/attachments/1045394533384462377/1084900598035513447/646808599204593683.png?size=128", - discordID: "0", - badges: [] - } - } - ] - }; - } - - return res; -} - -export async function addReview(review: any): Promise<Response | null> { - review.token = await getToken(); - - if (!review.token) { - showToast("Please authorize to add a review."); - authorize(); - return null; - } - - return fetch(API_URL + `/api/reviewdb/users/${review.userid}/reviews`, { - method: "PUT", - body: JSON.stringify(review), - headers: { - "Content-Type": "application/json", - } - }) - .then(r => r.json()) - .then(res => { - showToast(res.message); - return res ?? null; - }); -} - -export async function deleteReview(id: number): Promise<Response> { - return fetch(API_URL + `/api/reviewdb/users/${id}/reviews`, { - method: "DELETE", - headers: new Headers({ - "Content-Type": "application/json", - Accept: "application/json", - }), - body: JSON.stringify({ - token: await getToken(), - reviewid: id - }) - }).then(r => r.json()); -} - -export async function reportReview(id: number) { - const res = await fetch(API_URL + "/api/reviewdb/reports", { - method: "PUT", - headers: new Headers({ - "Content-Type": "application/json", - Accept: "application/json", - }), - body: JSON.stringify({ - reviewid: id, - token: await getToken() - }) - }).then(r => r.json()) as Response; - - showToast(res.message); -} - -export function getCurrentUserInfo(token: string): Promise<ReviewDBUser> { - return fetch(API_URL + "/api/reviewdb/users", { - body: JSON.stringify({ token }), - method: "POST", - }).then(r => r.json()); -} - -export async function readNotification(id: number) { - return fetch(API_URL + `/api/reviewdb/notifications?id=${id}`, { - method: "PATCH", - headers: { - "Authorization": await getToken() || "", - }, - }); -} diff --git a/src/plugins/reviewDB/settings.tsx b/src/plugins/reviewDB/settings.tsx deleted file mode 100644 index cf61a380..00000000 --- a/src/plugins/reviewDB/settings.tsx +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Vencord, a modification for Discord's desktop app - * Copyright (c) 2023 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 <https://www.gnu.org/licenses/>. -*/ - -import { definePluginSettings } from "@api/Settings"; -import { OptionType } from "@utils/types"; -import { Button } from "@webpack/common"; - -import { authorize, getToken } from "./auth"; - -export const settings = definePluginSettings({ - authorize: { - type: OptionType.COMPONENT, - description: "Authorize with ReviewDB", - component: () => ( - <Button onClick={authorize}> - Authorize with ReviewDB - </Button> - ) - }, - notifyReviews: { - type: OptionType.BOOLEAN, - description: "Notify about new reviews on startup", - default: true, - }, - showWarning: { - type: OptionType.BOOLEAN, - description: "Display warning to be respectful at the top of the reviews list", - default: true, - }, - hideTimestamps: { - type: OptionType.BOOLEAN, - description: "Hide timestamps on reviews", - default: false, - }, - hideBlockedUsers: { - type: OptionType.BOOLEAN, - description: "Hide reviews from blocked users", - default: true, - }, - website: { - type: OptionType.COMPONENT, - description: "ReviewDB website", - component: () => ( - <Button onClick={async () => { - let url = "https://reviewdb.mantikafasi.dev/"; - const token = await getToken(); - if (token) - url += "/api/redirect?token=" + encodeURIComponent(token); - - VencordNative.native.openExternal(url); - }}> - ReviewDB website - </Button> - ) - }, - supportServer: { - type: OptionType.COMPONENT, - description: "ReviewDB Support Server", - component: () => ( - <Button onClick={() => { - VencordNative.native.openExternal("https://discord.gg/eWPBSbvznt"); - }}> - ReviewDB Support Server - </Button> - ) - } -}).withPrivateSettings<{ - lastReviewId?: number; - reviewsDropdownState?: boolean; -}>(); diff --git a/src/plugins/reviewDB/style.css b/src/plugins/reviewDB/style.css deleted file mode 100644 index f4d890fd..00000000 --- a/src/plugins/reviewDB/style.css +++ /dev/null @@ -1,76 +0,0 @@ -[class|="section"]:not([class|="lastSection"]) + .vc-rdb-view { - margin-top: 12px; -} - -.vc-rdb-badge { - vertical-align: middle; - margin-left: 4px; -} - -.vc-rdb-input { - margin-top: 6px; - margin-bottom: 12px; - resize: none; - overflow: hidden; - background: transparent; - border: 1px solid var(--profile-message-input-border-color); -} - -.vc-rdb-modal-footer > div { - width: 100%; - margin: 6px 16px; -} - -/* When input becomes disabled(while sending review), input adds unneccesary padding to left, this prevents it */ -.vc-rdb-input > div > div { - padding-left: 0 !important; -} - -.vc-rdb-placeholder { - margin-bottom: 4px; - font-weight: bold; - font-style: italic; - color: var(--text-muted); -} - -.vc-rdb-input * { - font-size: 14px; -} - -.vc-rdb-modal-footer { - padding: 0; -} - -.vc-rdb-modal-footer .vc-rdb-input { - margin-bottom: 0; - background: var(--input-background); -} - -.vc-rdb-modal-footer [class|="pageControlContainer"] { - margin-top: 0; -} - -.vc-rdb-modal-header { - flex-grow: 1; -} - -.vc-rdb-modal-reviews { - margin-top: 16px; -} - -.vc-rdb-review { - margin-top: 8px; - margin-bottom: 8px; -} - -.vc-rdb-review-comment img { - vertical-align: text-top; -} - -.vc-rdb-review-comment { - overflow-y: hidden; - margin-top: 1px; - margin-bottom: 8px; - color: var(--text-normal); - font-size: 15px; -} diff --git a/src/plugins/reviewDB/utils.tsx b/src/plugins/reviewDB/utils.tsx deleted file mode 100644 index ab66d531..00000000 --- a/src/plugins/reviewDB/utils.tsx +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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 <https://www.gnu.org/licenses/>. -*/ - -import { classNameFactory } from "@api/Styles"; -import { UserStore } from "@webpack/common"; - -import { Auth } from "./auth"; -import { Review, UserType } from "./entities"; - -export const cl = classNameFactory("vc-rdb-"); - -export function canDeleteReview(profileId: string, review: Review) { - const myId = UserStore.getCurrentUser().id; - return ( - myId === profileId - || review.sender.discordID === profileId - || Auth.user?.type === UserType.Admin - ); -} From d81302f64c648f9eba0608ca0c9c6801a853f2b1 Mon Sep 17 00:00:00 2001 From: V <vendicated@riseup.net> Date: Mon, 9 Oct 2023 03:15:43 +0200 Subject: [PATCH 28/38] Revert mozilla store compliance changes This reverts commit 97b6699afefe373d510dda5589a0754a4b380153. Vencord is dropping support for the firefox extension, so these changes are now obsolete. revert so users can still install the extension manually and enjoy the full experience --- browser/background.js | 32 +++++++++++++++++++ browser/manifestv2.json | 6 +++- scripts/build/buildWeb.mjs | 6 ++-- src/components/VencordSettings/ThemesTab.tsx | 25 ++++----------- src/components/VencordSettings/VencordTab.tsx | 15 ++++----- src/plugins/_core/supportHelper.tsx | 19 +---------- src/utils/constants.ts | 2 -- 7 files changed, 53 insertions(+), 52 deletions(-) create mode 100644 browser/background.js diff --git a/browser/background.js b/browser/background.js new file mode 100644 index 00000000..1f2d5ec1 --- /dev/null +++ b/browser/background.js @@ -0,0 +1,32 @@ +/** + * @template T + * @param {T[]} arr + * @param {(v: T) => boolean} predicate + */ +function removeFirst(arr, predicate) { + const idx = arr.findIndex(predicate); + if (idx !== -1) arr.splice(idx, 1); +} + +chrome.webRequest.onHeadersReceived.addListener( + ({ responseHeaders, type, url }) => { + if (!responseHeaders) return; + + if (type === "main_frame") { + // In main frame requests, the CSP needs to be removed to enable fetching of custom css + // as desired by the user + removeFirst(responseHeaders, h => h.name.toLowerCase() === "content-security-policy"); + } else if (type === "stylesheet" && url.startsWith("https://raw.githubusercontent.com/")) { + // Most users will load css from GitHub, but GitHub doesn't set the correct content type, + // so we fix it here + removeFirst(responseHeaders, h => h.name.toLowerCase() === "content-type"); + responseHeaders.push({ + name: "Content-Type", + value: "text/css" + }); + } + return { responseHeaders }; + }, + { urls: ["https://raw.githubusercontent.com/*", "*://*.discord.com/*"], types: ["main_frame", "stylesheet"] }, + ["blocking", "responseHeaders"] +); diff --git a/browser/manifestv2.json b/browser/manifestv2.json index a6feada7..3cac9450 100644 --- a/browser/manifestv2.json +++ b/browser/manifestv2.json @@ -26,7 +26,11 @@ } ], - "web_accessible_resources": ["dist/*", "third-party/*"], + "background": { + "scripts": ["background.js"] + }, + + "web_accessible_resources": ["dist/Vencord.js", "dist/Vencord.css"], "browser_specific_settings": { "gecko": { diff --git a/scripts/build/buildWeb.mjs b/scripts/build/buildWeb.mjs index e4eeb53e..02e4da0c 100644 --- a/scripts/build/buildWeb.mjs +++ b/scripts/build/buildWeb.mjs @@ -145,11 +145,11 @@ async function loadDir(dir, basePath = "") { /** * @type {(target: string, files: string[]) => Promise<void>} */ -async function buildExtension(target, files, noMonaco = false) { +async function buildExtension(target, files) { const entries = { "dist/Vencord.js": await readFile("dist/extension.js"), "dist/Vencord.css": await readFile("dist/extension.css"), - ...(noMonaco ? {} : await loadDir("dist/monaco")), + ...await loadDir("dist/monaco"), ...Object.fromEntries(await Promise.all(RnNoiseFiles.map(async file => [`third-party/rnnoise/${file.replace(/^dist\//, "")}`, await readFile(`node_modules/@sapphi-red/web-noise-suppressor/${file}`)] ))), @@ -195,7 +195,7 @@ const appendCssRuntime = readFile("dist/Vencord.user.css", "utf-8").then(content await Promise.all([ appendCssRuntime, buildExtension("chromium-unpacked", ["modifyResponseHeaders.json", "content.js", "manifest.json", "icon.png"]), - buildExtension("firefox-unpacked", ["content.js", "manifestv2.json", "icon.png"], true), + buildExtension("firefox-unpacked", ["background.js", "content.js", "manifestv2.json", "icon.png"]), ]); Zip.sync.zip("dist/chromium-unpacked").compress().save("dist/extension.zip"); diff --git a/src/components/VencordSettings/ThemesTab.tsx b/src/components/VencordSettings/ThemesTab.tsx index 573f3b9f..f19cdcb8 100644 --- a/src/components/VencordSettings/ThemesTab.tsx +++ b/src/components/VencordSettings/ThemesTab.tsx @@ -18,11 +18,9 @@ import { useSettings } from "@api/Settings"; import { classNameFactory } from "@api/Styles"; -import { ErrorCard } from "@components/ErrorCard"; import { Flex } from "@components/Flex"; import { DeleteIcon } from "@components/Icons"; import { Link } from "@components/Link"; -import { IsFirefox } from "@utils/constants"; import { Margins } from "@utils/margins"; import { classes } from "@utils/misc"; import { showItemInFolder } from "@utils/native"; @@ -251,14 +249,12 @@ function ThemesTab() { > Load missing Themes </Button> - {!IsFirefox && ( - <Button - onClick={() => VencordNative.quickCss.openEditor()} - size={Button.Sizes.SMALL} - > - Edit QuickCSS - </Button> - )} + <Button + onClick={() => VencordNative.quickCss.openEditor()} + size={Button.Sizes.SMALL} + > + Edit QuickCSS + </Button> </> </Card> @@ -320,15 +316,6 @@ function ThemesTab() { return ( <SettingsTab title="Themes"> - {IsFirefox && ( - <ErrorCard> - <Forms.FormTitle tag="h5">Warning</Forms.FormTitle> - <Forms.FormText> - You are using Firefox. Expect the vast majority of themes to not work. - If this is a problem, use a chromium browser or Discord Desktop / Vesktop. - </Forms.FormText> - </ErrorCard> - )} <TabBar type="top" look="brand" diff --git a/src/components/VencordSettings/VencordTab.tsx b/src/components/VencordSettings/VencordTab.tsx index 520e4daa..a8e9ea5b 100644 --- a/src/components/VencordSettings/VencordTab.tsx +++ b/src/components/VencordSettings/VencordTab.tsx @@ -21,7 +21,6 @@ import { Settings, useSettings } from "@api/Settings"; import { classNameFactory } from "@api/Styles"; import DonateButton from "@components/DonateButton"; import { ErrorCard } from "@components/ErrorCard"; -import { IsFirefox } from "@utils/constants"; import { Margins } from "@utils/margins"; import { identity } from "@utils/misc"; import { relaunch, showItemInFolder } from "@utils/native"; @@ -110,14 +109,12 @@ function VencordSettings() { Restart Client </Button> )} - {!IsFirefox && ( - <Button - onClick={() => VencordNative.quickCss.openEditor()} - size={Button.Sizes.SMALL} - disabled={settingsDir === "Loading..."}> - Open QuickCSS File - </Button> - )} + <Button + onClick={() => VencordNative.quickCss.openEditor()} + size={Button.Sizes.SMALL} + disabled={settingsDir === "Loading..."}> + Open QuickCSS File + </Button> {!IS_WEB && ( <Button onClick={() => showItemInFolder(settingsDir)} diff --git a/src/plugins/_core/supportHelper.tsx b/src/plugins/_core/supportHelper.tsx index 2e86869d..674be8e5 100644 --- a/src/plugins/_core/supportHelper.tsx +++ b/src/plugins/_core/supportHelper.tsx @@ -17,7 +17,7 @@ */ import { DataStore } from "@api/index"; -import { Devs, IsFirefox, SUPPORT_CHANNEL_ID } from "@utils/constants"; +import { Devs, SUPPORT_CHANNEL_ID } from "@utils/constants"; import { isPluginDev } from "@utils/misc"; import { makeCodeblock } from "@utils/text"; import definePlugin from "@utils/types"; @@ -30,7 +30,6 @@ import plugins from "~plugins"; import settings from "./settings"; const REMEMBER_DISMISS_KEY = "Vencord-SupportHelper-Dismiss"; -const FIREFOX_DISMISS_KEY = "Vencord-Firefox-Warning-Dismiss"; const AllowedChannelIds = [ SUPPORT_CHANNEL_ID, @@ -116,22 +115,6 @@ ${makeCodeblock(enabledPlugins.join(", ") + "\n\n" + enabledApiPlugins.join(", " onConfirm: rememberDismiss }); } - - if (IsFirefox) { - const rememberDismiss = () => DataStore.set(FIREFOX_DISMISS_KEY, true); - - Alerts.show({ - title: "Hold on!", - body: <div> - <Forms.FormText>You are using Firefox.</Forms.FormText> - <Forms.FormText>Due to Firefox's stupid extension guidelines, most themes and many plugins will not function correctly.</Forms.FormText> - <Forms.FormText>Do not report bugs. Do not ask for help with broken plugins.</Forms.FormText> - <Forms.FormText>Instead, use a chromium browser, Discord Desktop, or Vesktop.</Forms.FormText> - </div>, - onCancel: rememberDismiss, - onConfirm: rememberDismiss - }); - } } } }); diff --git a/src/utils/constants.ts b/src/utils/constants.ts index e80298d1..6395ddfb 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -389,5 +389,3 @@ export const DevsById = /* #__PURE__*/ (() => .map(([_, v]) => [v.id, v] as const) )) )() as Record<string, Dev>; - -export const IsFirefox = IS_EXTENSION && navigator.userAgent.toLowerCase().includes("firefox"); From b59db2f8c2a7a06df4d71b791a032c67508e92ab Mon Sep 17 00:00:00 2001 From: V <vendicated@riseup.net> Date: Mon, 9 Oct 2023 03:49:33 +0200 Subject: [PATCH 29/38] Drop Firefox extension support Despite me already fixing all issues, mozilla is still giving me more trouble. Now they are asking me to provide them with testing credentials for discord. Not only do i not want to give them my account, it also isn't even possible because of how discord's login from new location verification works i am very tired of having to fight mozilla and their stupid guidelines / requests. publishing to amo is a nightmare. as such, official support for the extension is hereby dropped we cannot even distribute the extension ourselves because extensions NEED TO BE SIGNED to install them (unless you use firefox nightly). and guess how you sign? VIA THEIR STUPID STORE Options for firefox users: - use the UserScript - grab extension-firefox.zip from releases and install it on firefox nightly - make your own firefox developer account and manually sign the extension-firefox.zip and pray they sign it for you (they wouldn't sign my unlisted upload of it) - use a chromium browser --- .github/workflows/publish.yml | 7 ------- README.md | 24 +----------------------- package.json | 2 +- scripts/build/buildWeb.mjs | 7 +++++-- 4 files changed, 7 insertions(+), 33 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 9d56e9a9..e3639548 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -42,13 +42,6 @@ jobs: # Chrome cd dist/chromium-unpacked pnpx chrome-webstore-upload-cli@2.1.0 upload --auto-publish || EXIT_CODE=$? - - # Firefox - cd ../firefox-unpacked - npm i -g web-ext@7.4.0 web-ext-submit@7.4.0 - web-ext-submit || EXIT_CODE=$? - - exit $EXIT_CODE env: # Chrome EXTENSION_ID: ${{ secrets.CHROME_EXTENSION_ID }} diff --git a/README.md b/README.md index e848fd25..cd54fcdc 100644 --- a/README.md +++ b/README.md @@ -22,29 +22,7 @@ The cutest Discord client mod ## Installing / Uninstalling -Click the below button to install Vencord to the Discord Desktop app - -[![Download and run the Installer](https://img.shields.io/github/v/release/Vencord/Installer?label=Download%20Vencord%20Installer&style=for-the-badge)](https://github.com/Vencord/Installer#vencord-installer) - -## Installing on Browser - -[![Get it on the Firefox Webstore](https://blog.mozilla.org/addons/files/2015/11/get-the-addon.png)](https://addons.mozilla.org/en-GB/firefox/addon/vencord-web/) [![Get it on the Chrome Webstore](https://storage.googleapis.com/web-dev-uploads/image/WlD8wC6g8khYWPJUsQceQkhXSlv1/UV4C4ybeBTsZt43U4xis.png)](https://chrome.google.com/webstore/detail/vencord-web/cbghhgpcnddeihccjmnadmkaejncjndb) - -Or use the [UserScript](https://raw.githubusercontent.com/Vencord/builds/main/Vencord.user.js) - Please note that the CSS Editor, Themes loaded from remote sources and co. will not work in the UserScript. Use the extension if you need any of those - -<details> -<summary>Alternative Downloads</summary> - -## Vencord Desktop - -> **Warning** -> This is an alternative app. It currently doesn't support keybinds and possibly some more features. If you just want to install to the normal Discord Desktop app, scroll up - -As an alternative to the Discord Desktop app, Vencord also has its own standalone Desktop app that is snappier and lighter than Discord's official Desktop app - -[![Download Vencord Desktop](https://img.shields.io/github/v/release/Vencord/Desktop?label=Download%20Vencord%20Desktop&style=for-the-badge)](https://github.com/Vencord/Desktop#vencord-desktop) - -</details> +Visit https://vencord.dev/download ## Join our Support/Community Server diff --git a/package.json b/package.json index c005800a..176dd1a7 100644 --- a/package.json +++ b/package.json @@ -94,7 +94,7 @@ "build": { "overwriteDest": true }, - "sourceDir": "./dist/extension-v2-unpacked" + "sourceDir": "./dist/firefox-unpacked" }, "engines": { "node": ">=18", diff --git a/scripts/build/buildWeb.mjs b/scripts/build/buildWeb.mjs index 02e4da0c..353f4e06 100644 --- a/scripts/build/buildWeb.mjs +++ b/scripts/build/buildWeb.mjs @@ -198,5 +198,8 @@ await Promise.all([ buildExtension("firefox-unpacked", ["background.js", "content.js", "manifestv2.json", "icon.png"]), ]); -Zip.sync.zip("dist/chromium-unpacked").compress().save("dist/extension.zip"); -console.info("Packed Chromium Extension written to dist/extension.zip"); +Zip.sync.zip("dist/chromium-unpacked").compress().save("dist/extension-chrome.zip"); +console.info("Packed Chromium Extension written to dist/extension-chrome.zip"); + +Zip.sync.zip("dist/firefox-unpacked").compress().save("dist/extension-firefox.zip"); +console.info("Packed Firefox Extension written to dist/extension-firefox.zip"); From 1a36dbbc9bcdb9e3f1b31e690b902a7beb0ba2ae Mon Sep 17 00:00:00 2001 From: V <vendicated@riseup.net> Date: Mon, 9 Oct 2023 03:56:41 +0200 Subject: [PATCH 30/38] ci: cleanup publish workflow --- .github/workflows/publish.yml | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index e3639548..83236c11 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -36,19 +36,10 @@ jobs: - name: Publish extension run: | - # Do not fail so that even if chrome fails, firefox gets a shot. But also store exit code to fail workflow later - EXIT_CODE=0 - - # Chrome cd dist/chromium-unpacked - pnpx chrome-webstore-upload-cli@2.1.0 upload --auto-publish || EXIT_CODE=$? + pnpx chrome-webstore-upload-cli@2.1.0 upload --auto-publish env: - # Chrome EXTENSION_ID: ${{ secrets.CHROME_EXTENSION_ID }} CLIENT_ID: ${{ secrets.CHROME_CLIENT_ID }} CLIENT_SECRET: ${{ secrets.CHROME_CLIENT_SECRET }} REFRESH_TOKEN: ${{ secrets.CHROME_REFRESH_TOKEN }} - - # Firefox - WEB_EXT_API_KEY: ${{ secrets.WEBEXT_USER }} - WEB_EXT_API_SECRET: ${{ secrets.WEBEXT_SECRET }} From 925d7093355540f6b4e03a114bde5445a328725e Mon Sep 17 00:00:00 2001 From: V <vendicated@riseup.net> Date: Mon, 9 Oct 2023 03:57:44 +0200 Subject: [PATCH 31/38] bump to v1.5.6 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 176dd1a7..94133be8 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vencord", "private": "true", - "version": "1.5.5", + "version": "1.5.6", "description": "The cutest Discord client mod", "homepage": "https://github.com/Vendicated/Vencord#readme", "bugs": { From 5a97adb435f7f52af271169ebf57e32deaf49030 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Tue, 10 Oct 2023 04:58:46 -0300 Subject: [PATCH 32/38] Fix broken IgnoreActivities patch --- src/plugins/ignoreActivities/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/ignoreActivities/index.tsx b/src/plugins/ignoreActivities/index.tsx index 4809e889..e2888dd1 100644 --- a/src/plugins/ignoreActivities/index.tsx +++ b/src/plugins/ignoreActivities/index.tsx @@ -106,7 +106,7 @@ export default definePlugin({ } }, { - find: ".overlayBadge", + find: ".Messages.EMBEDDED_ACTIVITIES_HAVE_PLAYED_ONE_KNOWN", replacement: [ { match: /(?<=\(\)\.activityTitleText.+?children:(\i)\.name.*?}\),)/, From dcaf4aec97cb03b0ca77c310f7f8166f79cbdd44 Mon Sep 17 00:00:00 2001 From: sunnie <78964224+sunnniee@users.noreply.github.com> Date: Thu, 12 Oct 2023 04:30:32 +0300 Subject: [PATCH 33/38] fix moreUserTags (#1780) --- src/plugins/moreUserTags/index.tsx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/plugins/moreUserTags/index.tsx b/src/plugins/moreUserTags/index.tsx index 595a8edd..c8820f8b 100644 --- a/src/plugins/moreUserTags/index.tsx +++ b/src/plugins/moreUserTags/index.tsx @@ -53,8 +53,6 @@ interface TagSettings { [k: string]: TagSetting; } -const CLYDE_ID = "1081004946872352958"; - // PermissionStore.computePermissions is not the same function and doesn't work here const PermissionUtil = findByPropsLazy("computePermissions", "canEveryoneRole") as { computePermissions({ ...args }): bigint; @@ -215,7 +213,7 @@ export default definePlugin({ }, // add HTML data attributes (for easier theming) { - match: /children:\[(?=\i,\(0,\i\.jsx\)\("span",{className:\i\(\)\.botText,children:(\i)}\)\])/, + match: /children:\[(?=\i\?null:\i,\i,\(0,\i\.jsx\)\("span",{className:\i\(\)\.botText,children:(\i)}\)\])/, replace: "'data-tag':$1.toLowerCase(),children:[" } ], @@ -230,10 +228,10 @@ export default definePlugin({ }, // in the member list { - find: ".renderBot=function(){", + find: ".Messages.GUILD_OWNER,", replacement: { - match: /\.BOT;return null!=(\i)&&.{0,10}\?(.{0,50})\.botTag,type:\i/, - replace: ".BOT;var type=$self.getTag({...this.props,origType:$1.bot?0:null,location:'not-chat'});return type!==null?$2.botTag,type" + match: /(?<type>\i)=\(null==.{0,50}\.BOT,null!=(?<user>\i)&&\i\.bot/, + replace: "$<type> = $self.getTag({user: $<user>, channel: arguments[0].channel, origType: $<user>.bot ? 0 : null, location: 'not-chat' }), typeof $<type> === 'number'" } }, // pass channel id down props to be used in profiles @@ -253,7 +251,7 @@ export default definePlugin({ }, // in profiles { - find: ",botType:", + find: "showStreamerModeTooltip:", replacement: { match: /,botType:(\i\((\i)\)),/g, replace: ",botType:$self.getTag({user:$2,channelId:arguments[0].moreTags_channelId,origType:$1,location:'not-chat'})," @@ -341,15 +339,17 @@ export default definePlugin({ message, user, channelId, origType, location, channel }: { message?: Message, - user: User, + user: User & { isClyde(): boolean; }, channel?: Channel & { isForumPost(): boolean; }, channelId?: string; origType?: number; location: "chat" | "not-chat"; }): number | null { + if (!user) + return null; if (location === "chat" && user.id === "1") return Tag.Types.OFFICIAL; - if (user.id === CLYDE_ID) + if (user.isClyde()) return Tag.Types.AI; let type = typeof origType === "number" ? origType : null; From 926af0d1cd1ee44c6a4fe2c695fd59ff4431407e Mon Sep 17 00:00:00 2001 From: Marocco2 <marocco2@live.it> Date: Thu, 12 Oct 2023 04:05:46 +0200 Subject: [PATCH 34/38] feat(VcNarrator): add `{{DISPLAY_NAME}}` as placeholder (#1642) Co-authored-by: V <vendicated@riseup.net> --- src/plugins/vcNarrator/index.tsx | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/plugins/vcNarrator/index.tsx b/src/plugins/vcNarrator/index.tsx index 4447e080..39dabfc3 100644 --- a/src/plugins/vcNarrator/index.tsx +++ b/src/plugins/vcNarrator/index.tsx @@ -70,10 +70,11 @@ function clean(str: string) { .trim(); } -function formatText(str: string, user: string, channel: string) { +function formatText(str: string, user: string, channel: string, displayName: string) { return str .replaceAll("{{USER}}", clean(user) || (user ? "Someone" : "")) - .replaceAll("{{CHANNEL}}", clean(channel) || "channel"); + .replaceAll("{{CHANNEL}}", clean(channel) || "channel") + .replaceAll("{{DISPLAY_NAME}}", clean(displayName) || (displayName ? "Someone" : "")); } /* @@ -143,8 +144,9 @@ function updateStatuses(type: string, { deaf, mute, selfDeaf, selfMute, userId, function playSample(tempSettings: any, type: string) { const settings = Object.assign({}, Settings.plugins.VcNarrator, tempSettings); + const currentUser = UserStore.getCurrentUser(); - speak(formatText(settings[type + "Message"], UserStore.getCurrentUser().username, "general"), settings); + speak(formatText(settings[type + "Message"], currentUser.username, "general", (currentUser as any).globalName ?? currentUser.username), settings); } export default definePlugin({ @@ -172,9 +174,10 @@ export default definePlugin({ const template = Settings.plugins.VcNarrator[type + "Message"]; const user = isMe && !Settings.plugins.VcNarrator.sayOwnName ? "" : UserStore.getUser(userId).username; + const displayName = user && ((UserStore.getUser(userId) as any).globalName ?? user); const channel = ChannelStore.getChannel(id).name; - speak(formatText(template, user, channel)); + speak(formatText(template, user, channel, displayName)); // updateStatuses(type, state, isMe); } @@ -186,7 +189,7 @@ export default definePlugin({ if (!s) return; const event = s.mute || s.selfMute ? "unmute" : "mute"; - speak(formatText(Settings.plugins.VcNarrator[event + "Message"], "", ChannelStore.getChannel(chanId).name)); + speak(formatText(Settings.plugins.VcNarrator[event + "Message"], "", ChannelStore.getChannel(chanId).name, "")); }, AUDIO_TOGGLE_SELF_DEAF() { @@ -195,7 +198,7 @@ export default definePlugin({ if (!s) return; const event = s.deaf || s.selfDeaf ? "undeafen" : "deafen"; - speak(formatText(Settings.plugins.VcNarrator[event + "Message"], "", ChannelStore.getChannel(chanId).name)); + speak(formatText(Settings.plugins.VcNarrator[event + "Message"], "", ChannelStore.getChannel(chanId).name, "")); } }, @@ -312,8 +315,8 @@ export default definePlugin({ You can customise the spoken messages below. You can disable specific messages by setting them to nothing </Forms.FormText> <Forms.FormText> - The special placeholders <code>{"{{USER}}"}</code> and <code>{"{{CHANNEL}}"}</code>{" "} - will be replaced with the user's name (nothing if it's yourself) and the channel's name respectively + The special placeholders <code>{"{{USER}}"}</code>, <code>{"{{DISPLAY_NAME}}"}</code> and <code>{"{{CHANNEL}}"}</code>{" "} + will be replaced with the user's name (nothing if it's yourself), the user's display name and the channel's name respectively </Forms.FormText> {hasEnglishVoices && ( <> From 61cd7b4d99d4a4b90adb73530610d578a7b46334 Mon Sep 17 00:00:00 2001 From: Vendicated <vendicated@riseup.net> Date: Fri, 13 Oct 2023 03:49:58 +0200 Subject: [PATCH 35/38] arrpc: refactor for use in vesktop --- src/plugins/arRPC.web/index.tsx | 37 +++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/src/plugins/arRPC.web/index.tsx b/src/plugins/arRPC.web/index.tsx index f0d48414..1db0c457 100644 --- a/src/plugins/arRPC.web/index.tsx +++ b/src/plugins/arRPC.web/index.tsx @@ -58,6 +58,26 @@ export default definePlugin({ </> ), + async handleEvent(e: MessageEvent<any>) { + const data = JSON.parse(e.data); + + const { activity } = data; + const assets = activity?.assets; + + if (assets?.large_image) assets.large_image = await lookupAsset(activity.application_id, assets.large_image); + if (assets?.small_image) assets.small_image = await lookupAsset(activity.application_id, assets.small_image); + + if (activity) { + const appId = activity.application_id; + apps[appId] ||= await lookupApp(appId); + + const app = apps[appId]; + activity.name ||= app.name; + } + + FluxDispatcher.dispatch({ type: "LOCAL_ACTIVITY_UPDATE", ...data }); + }, + async start() { // ArmCord comes with its own arRPC implementation, so this plugin just confuses users if ("armcord" in window) return; @@ -65,22 +85,7 @@ export default definePlugin({ if (ws) ws.close(); ws = new WebSocket("ws://127.0.0.1:1337"); // try to open WebSocket - ws.onmessage = async e => { // on message, set status to data - const data = JSON.parse(e.data); - - if (data.activity?.assets?.large_image) data.activity.assets.large_image = await lookupAsset(data.activity.application_id, data.activity.assets.large_image); - if (data.activity?.assets?.small_image) data.activity.assets.small_image = await lookupAsset(data.activity.application_id, data.activity.assets.small_image); - - if (data.activity) { - const appId = data.activity.application_id; - apps[appId] ||= await lookupApp(appId); - - const app = apps[appId]; - data.activity.name ||= app.name; - } - - FluxDispatcher.dispatch({ type: "LOCAL_ACTIVITY_UPDATE", ...data }); - }; + ws.onmessage = this.handleEvent; const connectionSuccessful = await new Promise(res => setTimeout(() => res(ws.readyState === WebSocket.OPEN), 1000)); // check if open after 1s if (!connectionSuccessful) { From c2721f158f8796a0c8a8490e44116a3df5547b71 Mon Sep 17 00:00:00 2001 From: AutumnVN <autumnvnchino@gmail.com> Date: Fri, 13 Oct 2023 09:07:21 +0700 Subject: [PATCH 36/38] imageZoom: fix again (#1793) Co-authored-by: V <vendicated@riseup.net> --- src/plugins/imageZoom/index.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/plugins/imageZoom/index.tsx b/src/plugins/imageZoom/index.tsx index 60f8a22c..1f0d7e12 100644 --- a/src/plugins/imageZoom/index.tsx +++ b/src/plugins/imageZoom/index.tsx @@ -174,8 +174,8 @@ export default definePlugin({ find: "handleImageLoad=", replacement: [ { - match: /(render=function\(\){.{1,500}limitResponsiveWidth.{1,600})onMouseEnter:/, - replace: "$1...$self.makeProps(this),onMouseEnter:" + match: /showThumbhashPlaceholder:/, + replace: "...$self.makeProps(this),$&" }, { @@ -189,7 +189,6 @@ export default definePlugin({ } ] }, - { find: ".carouselModal,", replacement: { From a522eab40dff096ce6ff80dd9660dd0530e4d480 Mon Sep 17 00:00:00 2001 From: AutumnVN <autumnvnchino@gmail.com> Date: Fri, 13 Oct 2023 09:10:36 +0700 Subject: [PATCH 37/38] feat(plugin): NoMosaic (#1791) --- src/plugins/noMosaic/index.ts | 41 +++++++++++++++++++++++++++++++++ src/plugins/noMosaic/styles.css | 3 +++ 2 files changed, 44 insertions(+) create mode 100644 src/plugins/noMosaic/index.ts create mode 100644 src/plugins/noMosaic/styles.css diff --git a/src/plugins/noMosaic/index.ts b/src/plugins/noMosaic/index.ts new file mode 100644 index 00000000..60576ba9 --- /dev/null +++ b/src/plugins/noMosaic/index.ts @@ -0,0 +1,41 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2023 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { disableStyle, enableStyle } from "@api/Styles"; +import { Devs } from "@utils/constants"; +import definePlugin from "@utils/types"; + +import style from "./styles.css?managed"; + +export default definePlugin({ + name: "NoMosaic", + authors: [Devs.AutumnVN], + description: "Removes Discord new image mosaic", + tags: ["image", "mosaic", "media"], + patches: [{ + find: "Media Mosaic", + replacement: [ + { + match: /mediaLayoutType:\i\.\i\.MOSAIC/, + replace: 'mediaLayoutType:"RESPONSIVE"', + }, + { + match: /\i===\i\.\i\.MOSAIC/, + replace: "true", + }, + { + match: /null!==\(\i=\i\.get\(\i\)\)&&void 0!==\i\?\i:"INVALID"/, + replace: '"INVALID"', + }, + ], + }], + start() { + enableStyle(style); + }, + stop() { + disableStyle(style); + } +}); diff --git a/src/plugins/noMosaic/styles.css b/src/plugins/noMosaic/styles.css new file mode 100644 index 00000000..54616858 --- /dev/null +++ b/src/plugins/noMosaic/styles.css @@ -0,0 +1,3 @@ +[class^="nonMediaAttachmentsContainer-"] [class*="messageAttachment-"] { + position: relative; +} From 188d12d1a3de40f536011f679de1ec52be4dc32e Mon Sep 17 00:00:00 2001 From: AutumnVN <autumnvnchino@gmail.com> Date: Fri, 13 Oct 2023 09:26:18 +0700 Subject: [PATCH 38/38] roleColorEverywhere: role group color in thread/forum (#1778) --- src/plugins/roleColorEverywhere/index.tsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/plugins/roleColorEverywhere/index.tsx b/src/plugins/roleColorEverywhere/index.tsx index 8b256f40..e4fa4fe1 100644 --- a/src/plugins/roleColorEverywhere/index.tsx +++ b/src/plugins/roleColorEverywhere/index.tsx @@ -44,7 +44,7 @@ const settings = definePluginSettings({ export default definePlugin({ name: "RoleColorEverywhere", - authors: [Devs.KingFish, Devs.lewisakura], + authors: [Devs.KingFish, Devs.lewisakura, Devs.AutumnVN], description: "Adds the top role color anywhere possible", patches: [ // Chat Mentions @@ -78,6 +78,10 @@ export default definePlugin({ match: /(memo\(\(function\((\i)\).{300,500}CHANNEL_MEMBERS_A11Y_LABEL.{100,200}roleIcon.{5,20}null,).," \u2014 ",.\]/, replace: "$1$self.roleGroupColor($2)]" }, + { + match: /children:\[.," \u2014 ",.\]/, + replace: "children:[$self.roleGroupColor(arguments[0])]" + }, ], predicate: () => settings.store.memberList, }, @@ -105,7 +109,7 @@ export default definePlugin({ return colorString && parseInt(colorString.slice(1), 16); }, - roleGroupColor({ id, count, title, guildId }: { id: string; count: number; title: string; guildId: string; }) { + roleGroupColor({ id, count, title, guildId, label }: { id: string; count: number; title: string; guildId: string; label: string; }) { const guild = GuildStore.getGuild(guildId); const role = guild?.roles[id]; @@ -113,7 +117,7 @@ export default definePlugin({ color: role?.colorString, fontWeight: "unset", letterSpacing: ".05em" - }}>{title} — {count}</span>; + }}>{title ?? label} — {count}</span>; }, getVoiceProps({ user: { id: userId }, guildId }: { user: { id: string; }; guildId: string; }) {