diff --git a/src/plugins/memberCount/MemberCount.tsx b/src/plugins/memberCount/MemberCount.tsx index 3231d01ce..084e7ecc4 100644 --- a/src/plugins/memberCount/MemberCount.tsx +++ b/src/plugins/memberCount/MemberCount.tsx @@ -5,9 +5,10 @@ */ import { getCurrentChannel } from "@utils/discord"; +import { isObjectEmpty } from "@utils/misc"; import { SelectedChannelStore, Tooltip, useEffect, useStateFromStores } from "@webpack/common"; -import { ChannelMemberStore, cl, GuildMemberCountStore, numberFormat } from "."; +import { ChannelMemberStore, cl, GuildMemberCountStore, numberFormat, ThreadMemberListStore } from "."; import { OnlineMemberCountStore } from "./OnlineMemberCountStore"; export function MemberCount({ isTooltip, tooltipGuildId }: { isTooltip?: true; tooltipGuildId?: string; }) { @@ -30,10 +31,19 @@ export function MemberCount({ isTooltip, tooltipGuildId }: { isTooltip?: true; t () => ChannelMemberStore.getProps(guildId, currentChannel?.id) ); + const threadGroups = useStateFromStores( + [ThreadMemberListStore], + () => ThreadMemberListStore.getMemberListSections(currentChannel.id) + ); + if (!isTooltip && (groups.length >= 1 || groups[0].id !== "unknown")) { onlineCount = groups.reduce((total, curr) => total + (curr.id === "offline" ? 0 : curr.count), 0); } + if (!isTooltip && threadGroups && !isObjectEmpty(threadGroups)) { + onlineCount = Object.values(threadGroups).reduce((total, curr) => total + (curr.sectionId === "offline" ? 0 : curr.userIds.length), 0); + } + useEffect(() => { OnlineMemberCountStore.ensureCount(guildId); }, [guildId]); diff --git a/src/plugins/memberCount/index.tsx b/src/plugins/memberCount/index.tsx index 947d9deb1..1fa449889 100644 --- a/src/plugins/memberCount/index.tsx +++ b/src/plugins/memberCount/index.tsx @@ -32,6 +32,10 @@ export const GuildMemberCountStore = findStore("GuildMemberCountStore") as FluxS export const ChannelMemberStore = findStore("ChannelMemberStore") as FluxStore & { getProps(guildId: string, channelId: string): { groups: { count: number; id: string; }[]; }; }; +export const ThreadMemberListStore = findStore("ThreadMemberListStore") as FluxStore & { + getMemberListSections(channelId: string): { [sectionId: string]: { sectionId: string; userIds: string[]; }; }; +}; + const settings = definePluginSettings({ toolTip: { diff --git a/src/plugins/noMaskedUrlPaste/README.md b/src/plugins/noMaskedUrlPaste/README.md new file mode 100644 index 000000000..36707ac93 --- /dev/null +++ b/src/plugins/noMaskedUrlPaste/README.md @@ -0,0 +1,3 @@ +# NoMaskedUrlPaste + +Pasting a link while you have text selected will NOT paste your link as a masked link. diff --git a/src/plugins/noMaskedUrlPaste/index.ts b/src/plugins/noMaskedUrlPaste/index.ts new file mode 100644 index 000000000..64f522cae --- /dev/null +++ b/src/plugins/noMaskedUrlPaste/index.ts @@ -0,0 +1,23 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2023 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { Devs } from "@utils/constants.js"; +import definePlugin from "@utils/types"; + +export default definePlugin({ + name: "NoMaskedUrlPaste", + authors: [Devs.CatNoir], + description: "Pasting a link while having text selected will not paste as masked URL", + patches: [ + { + find: ".selection,preventEmojiSurrogates:", + replacement: { + match: /if\(null!=\i.selection&&\i.\i.isExpanded\(\i.selection\)\)/, + replace: "if(false)" + } + } + ], +}); diff --git a/src/plugins/noProfileThemes/index.ts b/src/plugins/noProfileThemes/index.ts index d4737d43e..7c440df80 100644 --- a/src/plugins/noProfileThemes/index.ts +++ b/src/plugins/noProfileThemes/index.ts @@ -19,7 +19,6 @@ import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; import { UserStore } from "@webpack/common"; -import virtualMerge from "virtual-merge"; export default definePlugin({ name: "NoProfileThemes", @@ -33,21 +32,7 @@ export default definePlugin({ replace: "$&$self.isCurrentUser(this.userId)&&" } }, - { - find: "UserProfileStore", - replacement: { - match: /(?<=getUserProfile\(\i\){return )(.+?)(?=})/, - replace: "$self.removeProfileThemes($1)" - } - } ], isCurrentUser: (userId: string) => userId === UserStore.getCurrentUser()?.id, - removeProfileThemes: (displayProfile: any) => { - if (displayProfile == null) return displayProfile; - - return displayProfile.userId === UserStore.getCurrentUser()?.id - ? displayProfile - : virtualMerge(displayProfile, { banner: undefined, profileEffectId: undefined }); - } }); diff --git a/src/plugins/pronoundb/index.ts b/src/plugins/pronoundb/index.ts index 51486e6ec..b0c5bfe6f 100644 --- a/src/plugins/pronoundb/index.ts +++ b/src/plugins/pronoundb/index.ts @@ -26,11 +26,6 @@ import { CompactPronounsChatComponentWrapper, PronounsChatComponentWrapper } fro import { useProfilePronouns } from "./pronoundbUtils"; import { settings } from "./settings"; -const PRONOUN_TOOLTIP_PATCH = { - match: /text:(.{0,10}.Messages\.USER_PROFILE_PRONOUNS)(?=,)/, - replace: '$& + (typeof vcPronounSource !== "undefined" ? ` (${vcPronounSource})` : "")' -}; - export default definePlugin({ name: "PronounDB", authors: [Devs.Tyman, Devs.TheKodeToad, Devs.Ven, Devs.Elvyra], @@ -52,7 +47,24 @@ export default definePlugin({ ] }, - // @TODO Patch discord pronoun hook in user profiles (useProfilePronouns) + { + find: ".Messages.USER_PROFILE_PRONOUNS", + group: true, + replacement: [ + { + match: /\.PANEL},/, + replace: "$&[vcPronoun,vcPronounSource,vcHasPendingPronouns]=$self.useProfilePronouns(arguments[0].user?.id)," + }, + { + match: /text:\i\.\i.Messages.USER_PROFILE_PRONOUNS/, + replace: '$&+vcHasPendingPronouns?"":` (${vcPronounSource})`' + }, + { + match: /(\.pronounsText.+?children:)(\i)/, + replace: "$1vcHasPendingPronouns?$2:vcPronoun" + } + ] + } ], settings, diff --git a/src/plugins/pronoundb/pronoundbUtils.ts b/src/plugins/pronoundb/pronoundbUtils.ts index 3c81e1474..a3c90c07c 100644 --- a/src/plugins/pronoundb/pronoundbUtils.ts +++ b/src/plugins/pronoundb/pronoundbUtils.ts @@ -20,13 +20,16 @@ import { debounce } from "@shared/debounce"; import { VENCORD_USER_AGENT } from "@shared/vencordUserAgent"; import { getCurrentChannel } from "@utils/discord"; import { useAwaiter } from "@utils/react"; +import { findStore } from "@webpack"; import { UserProfileStore, UserStore } from "@webpack/common"; import { settings } from "./settings"; import { CachePronouns, PronounCode, PronounMapping, PronounsResponse } from "./types"; -type PronounsWithSource = [string | null, string]; -const EmptyPronouns: PronounsWithSource = [null, ""]; +const UserSettingsAccountStore = findStore("UserSettingsAccountStore"); + +type PronounsWithSource = [pronouns: string | null, source: string, hasPendingPronouns: boolean]; +const EmptyPronouns: PronounsWithSource = [null, "", false]; export const enum PronounsFormat { Lowercase = "LOWERCASE", @@ -74,13 +77,15 @@ export function useFormattedPronouns(id: string, useGlobalProfile: boolean = fal onError: e => console.error("Fetching pronouns failed: ", e) }); + const hasPendingPronouns = UserSettingsAccountStore.getPendingPronouns() != null; + if (settings.store.pronounSource === PronounSource.PreferDiscord && discordPronouns) - return [discordPronouns, "Discord"]; + return [discordPronouns, "Discord", hasPendingPronouns]; if (result && result !== PronounMapping.unspecified) - return [result, "PronounDB"]; + return [result, "PronounDB", hasPendingPronouns]; - return [discordPronouns, "Discord"]; + return [discordPronouns, "Discord", hasPendingPronouns]; } export function useProfilePronouns(id: string, useGlobalProfile: boolean = false): PronounsWithSource { diff --git a/src/plugins/reviewDB/index.tsx b/src/plugins/reviewDB/index.tsx index e2897811d..eeefeb043 100644 --- a/src/plugins/reviewDB/index.tsx +++ b/src/plugins/reviewDB/index.tsx @@ -79,8 +79,22 @@ export default definePlugin({ { find: ".BITE_SIZE,user:", replacement: { - match: /(?<=\.BITE_SIZE,children:\[)\(0,\i\.jsx\)\(\i\.\i,\{user:(\i),/, - replace: "$self.BiteSizeReviewsButton({user:$1}),$&" + match: /{profileType:\i\.\i\.BITE_SIZE,children:\[/, + replace: "$&$self.BiteSizeReviewsButton({user:arguments[0].user})," + } + }, + { + find: ".FULL_SIZE,user:", + replacement: { + match: /{profileType:\i\.\i\.FULL_SIZE,children:\[/, + replace: "$&$self.BiteSizeReviewsButton({user:arguments[0].user})," + } + }, + { + find: ".PANEL,isInteractionSource:", + replacement: { + match: /{profileType:\i\.\i\.PANEL,children:\[/, + replace: "$&$self.BiteSizeReviewsButton({user:arguments[0].user})," } } ], diff --git a/src/plugins/showConnections/index.tsx b/src/plugins/showConnections/index.tsx index 03dd9c75b..627d4821f 100644 --- a/src/plugins/showConnections/index.tsx +++ b/src/plugins/showConnections/index.tsx @@ -168,7 +168,7 @@ export default definePlugin({ patches: [ { - find: '"BiteSizeProfileBody"', + find: ".hasAvatarForGuild(null==", replacement: { match: /currentUser:\i,guild:\i}\)(?<=user:(\i),bio:null==(\i)\?.+?)/, replace: "$&,$self.profilePopoutComponent({ user: $1, displayProfile: $2 })" diff --git a/src/webpack/common/types/utils.d.ts b/src/webpack/common/types/utils.d.ts index 7bce18360..f26325c2a 100644 --- a/src/webpack/common/types/utils.d.ts +++ b/src/webpack/common/types/utils.d.ts @@ -239,9 +239,26 @@ export interface Constants { FriendsSections: Record; } +export type ActiveView = LiteralUnion<"emoji" | "gif" | "sticker" | "soundboard", string>; + +export interface ExpressionPickerStoreState extends Record { + activeView: ActiveView | null; + lastActiveView: ActiveView | null; + activeViewType: any | null; + searchQuery: string; + isSearchSuggestion: boolean, + pickerId: string; +} + export interface ExpressionPickerStore { + openExpressionPicker(activeView: ActiveView, activeViewType?: any): void; closeExpressionPicker(activeViewType?: any): void; - openExpressionPicker(activeView: LiteralUnion<"emoji" | "gif" | "sticker", string>, activeViewType?: any): void; + toggleMultiExpressionPicker(activeViewType?: any): void; + toggleExpressionPicker(activeView: ActiveView, activeViewType?: any): void; + setExpressionPickerView(activeView: ActiveView): void; + setSearchQuery(searchQuery: string, isSearchSuggestion?: boolean): void; + useExpressionPickerStore(): ExpressionPickerStoreState; + useExpressionPickerStore(selector: (state: ExpressionPickerStoreState) => T): T; } export interface BrowserWindowFeatures { diff --git a/src/webpack/common/utils.ts b/src/webpack/common/utils.ts index 1207a53b1..5eb81ffcc 100644 --- a/src/webpack/common/utils.ts +++ b/src/webpack/common/utils.ts @@ -149,10 +149,14 @@ export const InviteActions = findByProps("resolveInvite"); export const IconUtils = findByProps("getGuildBannerURL", "getUserAvatarURL"); -// TODO: type export const ExpressionPickerStore: t.ExpressionPickerStore = mapMangledModule("expression-picker-last-active-view", { + openExpressionPicker: filters.byCode(/setState\({activeView:(?:(?!null)\i),activeViewType:/), closeExpressionPicker: filters.byCode("setState({activeView:null"), - openExpressionPicker: filters.byCode(/setState\({activeView:\i,activeViewType:/), + toggleMultiExpressionPicker: filters.byCode(".EMOJI,"), + toggleExpressionPicker: filters.byCode(/getState\(\)\.activeView===\i\?\i\(\):\i\(/), + setExpressionPickerView: filters.byCode(/setState\({activeView:\i,lastActiveView:/), + setSearchQuery: filters.byCode("searchQuery:"), + useExpressionPickerStore: filters.byCode("Object.is") }); export const PopoutActions: t.PopoutActions = mapMangledModule('type:"POPOUT_WINDOW_OPEN"', {