diff --git a/src/api/MessageAccessories.tsx b/src/api/MessageAccessories.tsx index d2bc081e7..5ac7c9531 100644 --- a/src/api/MessageAccessories.tsx +++ b/src/api/MessageAccessories.tsx @@ -17,6 +17,7 @@ */ import ErrorBoundary from "@components/ErrorBoundary"; +import { Logger } from "@utils/Logger"; import { JSX, ReactNode } from "react"; export type MessageAccessoryFactory = (props: Record) => ReactNode; @@ -46,23 +47,28 @@ export function _modifyAccessories( elements: JSX.Element[], props: Record ) { - for (const [key, accessory] of accessories.entries()) { - const res = ( - - - - ); + try { + for (const [key, accessory] of accessories.entries()) { + const res = ( + + + + ); - elements.splice( - accessory.position != null - ? accessory.position < 0 - ? elements.length + accessory.position - : accessory.position - : elements.length, - 0, - res - ); + elements.splice( + accessory.position != null + ? accessory.position < 0 + ? elements.length + accessory.position + : accessory.position + : elements.length, + 0, + res + ); + } + + return elements; + } catch (e) { + new Logger("MessageAccessories").error("Failed to modify message accessories", e); + return elements; } - - return elements; } diff --git a/src/plugins/_api/messageAccessories.ts b/src/plugins/_api/messageAccessories.ts index f0bf45896..f6f224ea7 100644 --- a/src/plugins/_api/messageAccessories.ts +++ b/src/plugins/_api/messageAccessories.ts @@ -28,7 +28,7 @@ export default definePlugin({ find: "#{intl::REMOVE_ATTACHMENT_BODY}", replacement: { match: /children:(\[[^\]]{0,100}?this.renderSuppressConfirmModal[^\]]{0,100}?\])/, - replace: "children:Vencord.Api.MessageAccessories._modifyAccessories($1,this.props)", + replace: "children:Vencord.Api.MessageAccessories._modifyAccessories($1,this?.props)", }, }, ], diff --git a/src/plugins/blurNsfw/index.ts b/src/plugins/blurNsfw/index.ts index f1c8ebf63..5d567d039 100644 --- a/src/plugins/blurNsfw/index.ts +++ b/src/plugins/blurNsfw/index.ts @@ -60,7 +60,7 @@ export default definePlugin({ replacement: [ { match: /(\.renderReactions\(\i\).+?className:)/, - replace: '$&(this.props?.channel?.nsfw?"vc-nsfw-img ":"")+' + replace: '$&(this?.props?.channel?.nsfw?"vc-nsfw-img ":"")+' } ] } diff --git a/src/plugins/callTimer/index.tsx b/src/plugins/callTimer/index.tsx index 6f9958130..bb1cb5dd5 100644 --- a/src/plugins/callTimer/index.tsx +++ b/src/plugins/callTimer/index.tsx @@ -66,6 +66,8 @@ function formatDuration(ms: number) { return res; } + + export default definePlugin({ name: "CallTimer", description: "Adds a timer to vcs", @@ -82,21 +84,17 @@ export default definePlugin({ replacement: { // in renderConnectionStatus() match: /(renderConnectionStatus\(\).{0,1000}?lineClamp:1,children:)(\i)(?=,|}\))/, - replace: "$1[$2,$self.renderTimer(this.props?.channel?.id)]" + replace: "$1[$2,$self.renderTimer({ channelId: this?.props?.channel?.id })]" } }], - renderTimer(channelId: string) { - return - - ; - }, + renderTimer: ErrorBoundary.wrap(({ channelId }: { channelId: string; }) => { + const time = useTimer({ deps: [channelId] }); - Timer({ channelId }: { channelId: string; }) { - const time = useTimer({ - deps: [channelId] - }); - - return

{formatDuration(time)}

; - } + return ( +

+ {formatDuration(time)} +

+ ); + }, { noop: true }), }); diff --git a/src/plugins/favGifSearch/index.tsx b/src/plugins/favGifSearch/index.tsx index 5061ba86d..efb96fad8 100644 --- a/src/plugins/favGifSearch/index.tsx +++ b/src/plugins/favGifSearch/index.tsx @@ -94,7 +94,7 @@ export default definePlugin({ // https://regex101.com/r/07gpzP/1 // ($1 renderHeaderContent=function { ... switch (x) ... case FAVORITES:return) ($2) ($3 case default: ... return r.jsx(($), {...props})) match: /(renderHeaderContent\(\).{1,150}FAVORITES:return)(.{1,150});(case.{1,200}default:.{0,50}?return\(0,\i\.jsx\)\((?\i\.\i),)/, - replace: "$1 this.state.resultType === 'Favorites' ? $self.renderSearchBar(this, $) : $2;$3" + replace: "$1 this?.state?.resultType === 'Favorites' ? $self.renderSearchBar(this, $) : $2;$3" }, { // to persist filtered favorites when component re-renders. diff --git a/src/plugins/fixImagesQuality/index.tsx b/src/plugins/fixImagesQuality/index.tsx index a10adb27e..91972bcd8 100644 --- a/src/plugins/fixImagesQuality/index.tsx +++ b/src/plugins/fixImagesQuality/index.tsx @@ -33,7 +33,7 @@ export default definePlugin({ find: ".handleImageLoad)", replacement: { match: /getSrc\(\i\)\{/, - replace: "$&var _vcSrc=$self.getSrc(this.props,arguments[1]);if(_vcSrc)return _vcSrc;" + replace: "$&var _vcSrc=$self.getSrc(this?.props,arguments[1]);if(_vcSrc)return _vcSrc;" } } ], diff --git a/src/plugins/gifPaste/index.ts b/src/plugins/gifPaste/index.ts index 40479790e..70b53f4e2 100644 --- a/src/plugins/gifPaste/index.ts +++ b/src/plugins/gifPaste/index.ts @@ -31,7 +31,7 @@ export default definePlugin({ find: "handleSelectGIF=", replacement: { match: /handleSelectGIF=(\i)=>\{/, - replace: "$&if (!this.props.className) return $self.handleSelect($1);" + replace: "$&if (!this?.props?.className) return $self.handleSelect($1);" } }], diff --git a/src/plugins/noProfileThemes/index.ts b/src/plugins/noProfileThemes/index.ts index a4fe648d0..7d81c098f 100644 --- a/src/plugins/noProfileThemes/index.ts +++ b/src/plugins/noProfileThemes/index.ts @@ -30,7 +30,7 @@ export default definePlugin({ find: "hasThemeColors(){", replacement: { match: /get canUsePremiumProfileCustomization\(\){return /, - replace: "$&$self.isCurrentUser(this.userId)&&" + replace: "$&$self.isCurrentUser(this?.userId)&&" } }, ], diff --git a/src/plugins/permissionsViewer/components/RolesAndUsersPermissions.tsx b/src/plugins/permissionsViewer/components/RolesAndUsersPermissions.tsx index 79442bf23..30aa4fb7a 100644 --- a/src/plugins/permissionsViewer/components/RolesAndUsersPermissions.tsx +++ b/src/plugins/permissionsViewer/components/RolesAndUsersPermissions.tsx @@ -250,9 +250,6 @@ function RoleContextMenu({ guild, roleId, onClose }: { guild: Guild; roleId: str label={getIntlMessage("VIEW_AS_ROLE")} icon={ViewAsRoleIcon} action={() => { - const role = GuildRoleStore.getRole(guild.id, roleId); - if (!role) return; - onClose(); FluxDispatcher.dispatch({ type: "IMPERSONATE_UPDATE", diff --git a/src/plugins/pinDms/index.tsx b/src/plugins/pinDms/index.tsx index 65699ebc8..3099a841b 100644 --- a/src/plugins/pinDms/index.tsx +++ b/src/plugins/pinDms/index.tsx @@ -114,7 +114,7 @@ export default definePlugin({ { // Override scrollToChannel to properly account for pinned channels match: /(?<=scrollTo\(\{to:\i\}\):\(\i\+=)(\d+)\*\(.+?(?=,)/, - replace: "$self.getScrollOffset(arguments[0],$1,this.props.padding,this.state.preRenderedChildren,$&)" + replace: "$self.getScrollOffset(arguments[0],$1,this?.props?.padding,this?.state?.preRenderedChildren,$&)" }, { match: /(scrollToChannel\(\i\){.{1,300})(this\.props\.privateChannelIds)/, diff --git a/src/plugins/showHiddenChannels/index.tsx b/src/plugins/showHiddenChannels/index.tsx index d0735efef..d38bd21b9 100644 --- a/src/plugins/showHiddenChannels/index.tsx +++ b/src/plugins/showHiddenChannels/index.tsx @@ -139,7 +139,7 @@ export default definePlugin({ "renderInviteButton", ].map(func => ({ match: new RegExp(`(?<=${func}\\(\\){)`, "g"), // Global because Discord has multiple declarations of the same functions - replace: "if($self.isHiddenChannel(this.props.channel))return null;" + replace: "if($self.isHiddenChannel(this?.props?.channel))return null;" })) ] }, @@ -149,7 +149,7 @@ export default definePlugin({ // Render null instead of the buttons if the channel is hidden replacement: { match: /(?<=renderOpenChatButton(?:",|=)\(\)=>{)/, - replace: "if($self.isHiddenChannel(this.props.channel))return null;" + replace: "if($self.isHiddenChannel(this?.props?.channel))return null;" } }, { @@ -245,11 +245,11 @@ export default definePlugin({ }, { match: /(?<=renderSidebar\(\){)/, - replace: "if($self.isHiddenChannel(this.props.channel))return null;" + replace: "if($self.isHiddenChannel(this?.props?.channel))return null;" }, { match: /(?<=renderChat\(\){)/, - replace: "if($self.isHiddenChannel(this.props.channel))return $self.HiddenChannelLockScreen(this.props.channel);" + replace: "if($self.isHiddenChannel(this?.props?.channel))return $self.HiddenChannelLockScreen(this?.props?.channel);" } ] }, @@ -355,22 +355,22 @@ export default definePlugin({ { // Render our HiddenChannelLockScreen component instead of the main voice channel component match: /renderContent\(\i\){.+?this\.renderVoiceChannelEffects.+?children:/, - replace: "$&!this.props.inCall&&$self.isHiddenChannel(this.props.channel,true)?$self.HiddenChannelLockScreen(this.props.channel):" + replace: "$&!this?.props?.inCall&&$self.isHiddenChannel(this?.props?.channel,true)?$self.HiddenChannelLockScreen(this?.props?.channel):" }, { // Disable gradients for the HiddenChannelLockScreen of voice channels match: /renderContent\(\i\){.+?disableGradients:/, - replace: "$&!this.props.inCall&&$self.isHiddenChannel(this.props.channel,true)||" + replace: "$&!this?.props?.inCall&&$self.isHiddenChannel(this?.props?.channel,true)||" }, { // Disable useless components for the HiddenChannelLockScreen of voice channels match: /(?:{|,)render(?!Header|ExternalHeader).{0,30}?:/g, - replace: "$&!this.props.inCall&&$self.isHiddenChannel(this.props.channel,true)?()=>null:" + replace: "$&!this?.props?.inCall&&$self.isHiddenChannel(this?.props?.channel,true)?()=>null:" }, { // Disable bad CSS class which mess up hidden voice channels styling match: /(?=\i\|\|\i!==\i\.\i\.FULL_SCREEN.{0,100}?this\._callContainerRef)/, - replace: '$&!this.props.inCall&&$self.isHiddenChannel(this.props.channel,true)?"":' + replace: '$&!this?.props?.inCall&&$self.isHiddenChannel(this?.props?.channel,true)?"":' } ] }, diff --git a/src/plugins/themeAttributes/index.ts b/src/plugins/themeAttributes/index.ts index f7ac5ad62..d6e2089b9 100644 --- a/src/plugins/themeAttributes/index.ts +++ b/src/plugins/themeAttributes/index.ts @@ -24,7 +24,7 @@ export default definePlugin({ find: ".tabBarRef", replacement: { match: /style:this\.getStyle\(\),role:"tab"/, - replace: "$&,'data-tab-id':this.props.id" + replace: "$&,'data-tab-id':this?.props?.id" } }, @@ -59,7 +59,7 @@ export default definePlugin({ ], getAvatarStyles(src: string | null) { - if (!src || src.startsWith("data:")) return {}; + if (typeof src !== "string" || src.startsWith("data:")) return {}; return Object.fromEntries( [128, 256, 512, 1024, 2048, 4096].map(size => [ diff --git a/src/plugins/whoReacted/index.tsx b/src/plugins/whoReacted/index.tsx index c257017ea..862b5d315 100644 --- a/src/plugins/whoReacted/index.tsx +++ b/src/plugins/whoReacted/index.tsx @@ -25,6 +25,29 @@ import definePlugin from "@utils/types"; import { CustomEmoji, Message, ReactionEmoji, User } from "@vencord/discord-types"; import { ChannelStore, Constants, FluxDispatcher, React, RestAPI, useEffect, useLayoutEffect, UserStore, UserSummaryItem } from "@webpack/common"; +interface ReactionCacheEntry { + fetched: boolean; + users: Map; +} + +interface ReactionProps { + message: Message; + readOnly: boolean; + isLurking: boolean; + isPendingMember: boolean; + useChatFontScaling: boolean; + emoji: CustomEmoji; + count: number; + burst_user_ids: any[]; + burst_count: number; + burst_colors: any[]; + burst_me: boolean; + me: boolean; + type: number; + hideEmoji: boolean; + remainingBurstCurrency: number; +} + let Scroll: any = null; const queue = new Queue(); let reactions: Record = {}; @@ -75,6 +98,46 @@ function handleClickAvatar(event: React.UIEvent) { event.stopPropagation(); } +function ReactionUsers({ message, emoji, type }: ReactionProps) { + const forceUpdate = useForceUpdater(); + + useLayoutEffect(() => { // bc need to prevent autoscrolling + if (Scroll?.scrollCounter > 0) { + Scroll.setAutomaticAnchor(null); + } + }); + + useEffect(() => { + const cb = (e: any) => { + if (e?.messageId === message.id) + forceUpdate(); + }; + FluxDispatcher.subscribe("MESSAGE_REACTION_ADD_USERS", cb); + + return () => FluxDispatcher.unsubscribe("MESSAGE_REACTION_ADD_USERS", cb); + }, [message.id, forceUpdate]); + + const reactions = getReactionsWithQueue(message, emoji, type); + const users = Array.from(reactions, ([id]) => UserStore.getUser(id)).filter(Boolean); + + return ( +
+
+ +
+
+ ); +} + export default definePlugin({ name: "WhoReacted", description: "Renders the avatars of users who reacted to a message", @@ -86,7 +149,7 @@ export default definePlugin({ find: ",reactionRef:", replacement: { match: /(\i)\?null:\(0,\i\.jsx\)\(\i\.\i,{className:\i\.reactionCount,.*?}\),/, - replace: "$&$1?null:$self.renderUsers(this.props)," + replace: "$&$1?null:$self.renderUsers(this?.props)," } }, { @@ -106,82 +169,17 @@ export default definePlugin({ } ], + renderUsers: ErrorBoundary.wrap((props: ReactionProps) => { + return props.message.reactions.length > 10 + ? null + : ; + }, { noop: true }), + setScrollObj(scroll: any) { Scroll = scroll; }, - renderUsers(props: RootObject) { - return props.message.reactions.length > 10 ? null : ( - - - - ); - }, - - UsersComponent({ message, emoji, type }: RootObject) { - const forceUpdate = useForceUpdater(); - - useLayoutEffect(() => { // bc need to prevent autoscrolling - if (Scroll?.scrollCounter > 0) { - Scroll.setAutomaticAnchor(null); - } - }); - - useEffect(() => { - const cb = (e: any) => { - if (e?.messageId === message.id) - forceUpdate(); - }; - FluxDispatcher.subscribe("MESSAGE_REACTION_ADD_USERS", cb); - - return () => FluxDispatcher.unsubscribe("MESSAGE_REACTION_ADD_USERS", cb); - }, [message.id, forceUpdate]); - - const reactions = getReactionsWithQueue(message, emoji, type); - const users = Array.from(reactions, ([id]) => UserStore.getUser(id)).filter(Boolean); - - return ( -
-
- -
-
- ); - }, - set reactions(value: any) { reactions = value; } }); - -interface ReactionCacheEntry { - fetched: boolean; - users: Map; -} - -interface RootObject { - message: Message; - readOnly: boolean; - isLurking: boolean; - isPendingMember: boolean; - useChatFontScaling: boolean; - emoji: CustomEmoji; - count: number; - burst_user_ids: any[]; - burst_count: number; - burst_colors: any[]; - burst_me: boolean; - me: boolean; - type: number; - hideEmoji: boolean; - remainingBurstCurrency: number; -}