diff --git a/src/equicordplugins/contentWarning/index.tsx b/src/equicordplugins/contentWarning/index.tsx index 52ccfe1b6..46336fb2d 100644 --- a/src/equicordplugins/contentWarning/index.tsx +++ b/src/equicordplugins/contentWarning/index.tsx @@ -4,8 +4,11 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ +import "./styles.css"; + import { DataStore } from "@api/index"; import { definePluginSettings } from "@api/Settings"; +import { classNameFactory } from "@api/Styles"; import { Flex } from "@components/Flex"; import { HeadingTertiary } from "@components/Heading"; import { DeleteIcon } from "@components/Icons"; @@ -14,6 +17,8 @@ import { useForceUpdater } from "@utils/react"; import definePlugin, { OptionType } from "@utils/types"; import { Button, TextInput, useState } from "@webpack/common"; +const cl = classNameFactory("vc-content-warning-"); + const WORDS_KEY = "ContentWarning_words"; let triggerWords = [""]; @@ -29,26 +34,31 @@ function safeMatchesRegex(s: string, r: string) { function TriggerContainer({ child }) { const [visible, setVisible] = useState(false); + const { onClick } = settings.store; + + const className = onClick ? cl("container") : ""; if (visible) { return child; } else { - return (
setVisible(true)}> -
onClick && setVisible(true)} onMouseEnter={event => { - event.currentTarget.style.filter = "none"; + if (!onClick) { + event.currentTarget.className = cl("enter"); + } }} onMouseLeave={event => { - event.currentTarget.style.filter = "blur(4px) brightness(70%)"; + if (!onClick) { + event.currentTarget.className = cl("leave"); + } }} > {child} -
-
); + + ); } } @@ -118,36 +128,43 @@ function FlaggedWords() { ); }); - return (<> - Flagged Words - {inputs} - ); + return ( + <> + Flagged Words + {inputs} + + ); } const settings = definePluginSettings({ flagged: { type: OptionType.COMPONENT, component: () => , + }, + onClick: { + type: OptionType.BOOLEAN, + description: "Only show trigger content on click instead of hover", + default: false, } }); export default definePlugin({ name: "ContentWarning", authors: [EquicordDevs.camila314], - description: "Allows you to specify certain trigger words that will be blurred by default. Hovering on the blurred content will reveal it.", + description: "Allows you to specify certain trigger words that will be blurred by default. Hovering/Clicking on the blurred content will reveal it.", settings, patches: [ { find: ".VOICE_HANGOUT_INVITE?", replacement: { match: /(compact:\i}=(\i).+?)(\(0,.+\}\)\]\}\))/, - replace: "$1 $self.modify($2,$3)" + replace: "$1 $self.modify(arguments[0].message,$3)" } } ], - modify(e, child) { - if (triggerWords.some(word => safeMatchesRegex(e.message.content, word))) { + modify(message, child) { + if (triggerWords.some(w => safeMatchesRegex(message.content, w))) { return ; } else { return child; diff --git a/src/equicordplugins/contentWarning/styles.css b/src/equicordplugins/contentWarning/styles.css new file mode 100644 index 000000000..898bd336b --- /dev/null +++ b/src/equicordplugins/contentWarning/styles.css @@ -0,0 +1,13 @@ +.vc-content-warning-container { + filter: blur(5px) brightness(30%); + transition: filter 0.2s ease-in-out; + cursor: pointer; +} + +.vc-content-warning-enter { + filter: none; +} + +.vc-content-warning-leave { + filter: blur(5px) brightness(30%); +} \ No newline at end of file diff --git a/src/equicordplugins/sidebarChat/index.tsx b/src/equicordplugins/sidebarChat/index.tsx index 821ac8c8d..83936d728 100644 --- a/src/equicordplugins/sidebarChat/index.tsx +++ b/src/equicordplugins/sidebarChat/index.tsx @@ -9,9 +9,9 @@ import "./styles.css"; import { NavContextMenuPatchCallback } from "@api/ContextMenu"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; -import { getCurrentChannel, getCurrentGuild } from "@utils/discord"; +import { getCurrentChannel } from "@utils/discord"; import definePlugin from "@utils/types"; -import { Channel, User } from "@vencord/discord-types"; +import { Channel, Guild, User } from "@vencord/discord-types"; import { DefaultExtractAndLoadChunksRegex, extractAndLoadChunksLazy, @@ -26,14 +26,19 @@ import { ChannelRouter, ChannelStore, FluxDispatcher, + GuildStore, Menu, MessageActions, MessageStore, PermissionsBits, PermissionStore, PopoutActions, - React, + RelationshipStore, + SelectedChannelStore, + SelectedGuildStore, + useCallback, useEffect, + useLayoutEffect, UserStore, useState, useStateFromStores @@ -50,61 +55,87 @@ const { HeaderBar, /* HeaderBarIcon*/ } = mapMangledModuleLazy(".themedMobile]:" // from toolbox const HeaderBarIcon = findComponentByCodeLazy(".HEADER_BAR_BADGE_TOP:", '.iconBadge,"top"'); -const ArrowsLeftRightIcon = () => { - return ; +const { ForumView } = mapMangledModuleLazy("forum-grid-header-section-", { + ForumView: filters.byCode("sidebarState") +}); + +const ArrowsLeftRightIcon = ({ color, ...rest }) => { + return ( + + ); }; const WindowLaunchIcon = findComponentByCodeLazy("1-1h6a1 1 0 1 0 0-2H5Z"); const XSmallIcon = findComponentByCodeLazy("1.4L12 13.42l5.3 5.3Z"); const Chat = findComponentByCodeLazy("filterAfterTimestamp:", "chatInputType"); -const Resize = findComponentByCodeLazy("sidebarType:", ".RESIZE_HANDLE_WIDTH"); +const Resize = findComponentByCodeLazy("sidebarType:", "RESIZE_HANDLE_WIDTH)"); const ChannelHeader = findComponentByCodeLazy(".forumPostTitle]:", '"channel-".concat'); const PopoutWindow = findComponentByCodeLazy("Missing guestWindow reference"); const FullChannelView = findComponentByCodeLazy("showFollowButton:(null"); +const WanderingCubesLoading = findComponentByCodeLazy('="wanderingCubes"'); // love const ppStyle = findLazy(m => m?.popoutContent && Object.keys(m).length === 1); const ChatInputTypes = findByPropsLazy("FORM", "NORMAL"); const Sidebars = findByPropsLazy("ThreadSidebar", "MessageRequestSidebar"); +const ChatClasses = findByPropsLazy("threadSidebarOpen"); const ChannelSectionStore = findStoreLazy("ChannelSectionStore"); -const requireChannelContextMenu = extractAndLoadChunksLazy(["&&this.handleActivitiesPopoutClose(),"], new RegExp(DefaultExtractAndLoadChunksRegex.source + ".{1,150}isFavorite")); +const requireChannelContextMenu = extractAndLoadChunksLazy( + ["&&this.handleActivitiesPopoutClose(),"], + new RegExp(DefaultExtractAndLoadChunksRegex.source + ".{1,150}isFavorite") +); +const requireForumView = extractAndLoadChunksLazy( + ["Missing channel in Channel.renderHeaderToolbar"], + new RegExp(DefaultExtractAndLoadChunksRegex.source + '.{1,150}name:"ForumChannel"') +); -interface ContextMenuProps { - channel: Channel; - guildId?: string; - user: User; -} +const MakeContextMenu = (id: string, guildId: string | null) => { + return ( + { + FluxDispatcher.dispatch({ + // @ts-ignore + type: "VC_SIDEBAR_CHAT_NEW", + guildId, + id, + }); + }} + /> + ); +}; -function MakeContextCallback(name: "user" | "channel"): NavContextMenuPatchCallback { - return (children, { user, channel }: ContextMenuProps) => { - const isUser = name === "user"; - if (isUser && !user) return; - if (!isUser && (!channel || channel.type === 4)) return; +const UserContextPatch: NavContextMenuPatchCallback = (children, args: { user: User; }) => { + const checks = [ + args.user, + args.user.id !== UserStore.getCurrentUser().id, + ]; + if (checks.some(check => !check)) return; + children.push(MakeContextMenu(args.user.id, null)); +}; - if (isUser && user.id === UserStore.getCurrentUser().id) return; - if (!isUser && (!PermissionStore.can(PermissionsBits.VIEW_CHANNEL, channel) && channel.type !== 3)) return; - - children.push( - { - FluxDispatcher.dispatch({ - // @ts-ignore - type: "NEW_SIDEBAR_CHAT", - isUser, - guildId: channel.guild_id, - id: isUser ? user.id : channel.id, - }); - }} - /> - ); - }; -} +const ChannelContextPatch: NavContextMenuPatchCallback = (children, args: { channel: Channel; }) => { + const checks = [ + args.channel, + args.channel.type !== 4, // categories + PermissionStore.can(PermissionsBits.VIEW_CHANNEL, args.channel), + ]; + if (checks.some(check => !check)) return; + children.push(MakeContextMenu(args.channel.id, args.channel.guild_id)); +}; export default definePlugin({ name: "SidebarChat", @@ -113,140 +144,204 @@ export default definePlugin({ patches: [ { find: 'case"pendingFriends":', - replacement: { - match: /return(\(0,\i\.jsxs?\)\(\i\.\i,{}\))}/, - replace: "return [$1, $self.renderSidebar()]}" - } + group: true, + replacement: [ + { + match: /channel_renderer"\);/, + replace: "$&const vc_SidebarChat=$self.renderSidebar();" + }, + { + match: /return(\(0,\i\.jsxs?\)\(\i\.\i,{}\))}/, + replace: "return [$1, vc_SidebarChat]}" + }, + { + match: /(case \i\.\i.+?return)(.+?);(?=.+?params\.messageId)(?<=channel_renderer".+?)/g, + replace: "$1[$2, vc_SidebarChat];", + predicate: () => settings.store.patchCommunity + } + ] }, ], settings, contextMenus: { - "user-context": MakeContextCallback("user"), - "channel-context": MakeContextCallback("channel"), - "thread-context": MakeContextCallback("channel"), - "gdm-context": MakeContextCallback("channel"), + "user-context": UserContextPatch, + "channel-context": ChannelContextPatch, + "thread-context": ChannelContextPatch, + "gdm-context": ChannelContextPatch, }, - renderSidebar: ErrorBoundary.wrap(() => { - const { guild, channel, /* width*/ } = useStateFromStores([SidebarStore], () => SidebarStore.getFullState()); - const [width, setWidth] = useState(0); + toolboxActions: { + "Open Previous Chat"() { + FluxDispatcher.dispatch({ + // @ts-ignore + type: "VC_SIDEBAR_CHAT_PREVIOUS", + }); + } + }, + + renderSidebar() { + const { guild, channel /* width*/ } = useStateFromStores( + [SidebarStore, GuildStore, ChannelStore], () => { + const { channelId, guildId } = SidebarStore.getState(); + return { + guild: GuildStore.getGuild(guildId), + channel: ChannelStore.getChannel(channelId) + }; + }, [] + ); const [channelSidebar, guildSidebar] = useStateFromStores( - [ChannelSectionStore], - () => [ - ChannelSectionStore.getSidebarState(getCurrentChannel()?.id), - ChannelSectionStore.getGuildSidebarState(getCurrentGuild()?.id), - ] + [ChannelSectionStore, SelectedChannelStore, ChannelStore], () => { + const currentChannelId = SelectedChannelStore.getChannelId(); + const currentGuildId = SelectedGuildStore.getGuildId(); + return [ + ChannelSectionStore.getSidebarState(currentChannelId), + ChannelSectionStore.getGuildSidebarState(currentGuildId), + ]; + }, [] ); useEffect(() => { - if (channel) { - if (MessageStore.getLastMessage(channel.id)) return; - MessageActions.fetchMessages({ - channelId: channel.id, - limit: 50, - }); - } - }, [channel]); + if (!channel?.id || MessageStore.getLastMessage(channel.id)) return; + MessageActions.fetchMessages({ + channelId: channel.id, + limit: 50, + }); + }, [channel?.id]); - useEffect(() => { - if (width === 0) setWidth(window.innerWidth); + const [width, setWidth] = useState(window.innerWidth); + + useLayoutEffect(() => { const handleResize = () => setWidth(window.innerWidth); window.addEventListener("resize", handleResize); - return () => { - window.removeEventListener("resize", handleResize); - }; + return () => window.removeEventListener("resize", handleResize); }, []); + const [View, setViewComponent] = useState(null); + + useEffect(() => { + if (!channel) return; + + // @ts-expect-error + if (channel.isForumLikeChannel()) { + requireForumView().then(() => { + setViewComponent( + + ); + }); + + setViewComponent( +
+ +
+ ); + } else { + setViewComponent( + + ); + } + }, [channel]); + if (!channel || channelSidebar || guildSidebar) return null; return ( - - - { - const currentChannel = getCurrentChannel()!; - FluxDispatcher.dispatch({ - // @ts-ignore - type: "NEW_SIDEBAR_CHAT", - isUser: currentChannel.id === "1", - guildId: currentChannel?.guild_id, - id: currentChannel.id, - }); - ChannelRouter.transitionToChannel(channel.id); - }} - /> - { - await requireChannelContextMenu(); - PopoutActions.open( - `DISCORD_VC_SC-${channel.id}`, - () => , - { - defaultWidth: 854, - defaultHeight: 480 - }); - }} - /> - { - FluxDispatcher.dispatch({ - // @ts-ignore - type: "CLOSE_SIDEBAR_CHAT", - }); - }} - /> - - } + + - - - - +
+ {View} + + ); - }), + }, }); -const RenderPopout = ErrorBoundary.wrap(({ channel }: { channel: Channel; }) => { +const Header = ({ guild, channel }: { guild: Guild; channel: Channel; }) => { + const recipientId = channel.isPrivate() ? channel.getRecipientId() as string : null; + + const name = useStateFromStores([UserStore, RelationshipStore], () => { + if (!recipientId || channel.name) return channel.name; + + const user = UserStore.getUser(recipientId); + return RelationshipStore.getNickname(recipientId) || user?.globalName || user?.username; + }, [recipientId, channel.name]); + + const parentChannel = useStateFromStores( + [ChannelStore], () => ChannelStore.getChannel(channel?.parent_id), + [channel?.parent_id] + ); + + // @ts-ignore + const closeSidebar = () => FluxDispatcher.dispatch({ type: "VC_SIDEBAR_CHAT_CLOSE", }); + + const openPopout = useCallback(async () => { + await requireChannelContextMenu(); + PopoutActions.open( + `DISCORD_VC_SC-${channel.id}`, + () => , + { + defaultWidth: 854, + defaultHeight: 480, + } + ); + }, [channel, name]); + + const switchChannels = useCallback(() => { + const mainChannel = getCurrentChannel()!; + FluxDispatcher.dispatch({ + // @ts-ignore + type: "VC_SIDEBAR_CHAT_NEW", + guildId: mainChannel.guild_id, + id: mainChannel.id, + }); + ChannelRouter.transitionToChannel(channel.id); + }, [channel.id]); + + return ( + + + + + + } + > + + + ); +}; + +const RenderPopout = ErrorBoundary.wrap(({ channel, name }: { channel: Channel; name: string; }) => { // Copy from an unexported function of the one they use in the experiment // right click a channel and search withTitleBar:!0,windowKey - const { Provider } = React.createContext(undefined); - const selectedChannel = ChannelStore.getChannel(channel.id); return ( - - - + ); }); diff --git a/src/equicordplugins/sidebarChat/store.ts b/src/equicordplugins/sidebarChat/store.ts index d1cfe32f7..b91591f5e 100644 --- a/src/equicordplugins/sidebarChat/store.ts +++ b/src/equicordplugins/sidebarChat/store.ts @@ -7,86 +7,84 @@ import { definePluginSettings } from "@api/Settings"; import { proxyLazy } from "@utils/lazy"; import { OptionType } from "@utils/types"; -import { FluxEmitter, FluxStore } from "@vencord/discord-types"; -import { findByPropsLazy } from "@webpack"; -import { ChannelActionCreators, ChannelStore, FluxDispatcher, GuildStore } from "@webpack/common"; +import { Flux as TFlux } from "@vencord/discord-types"; +import { ChannelActionCreators, Flux as FluxWP, FluxDispatcher } from "@webpack/common"; -interface IFlux { - PersistedStore: typeof FluxStore; - Emitter: FluxEmitter; +interface IFlux extends TFlux { + PersistedStore: TFlux["Store"]; } -const Flux: IFlux = findByPropsLazy("connectStores"); export const settings = definePluginSettings({ persistSidebar: { type: OptionType.BOOLEAN, description: "Keep the sidebar chat open across Discord restarts", default: true, + }, + patchCommunity: { + type: OptionType.BOOLEAN, + description: "Patch things like the Channel Browser or Members tab that community servers have.", + default: true, + restartNeeded: true, } }); -interface SidebarData { - isUser: boolean; - guildId: string; - id: string; -} - export const SidebarStore = proxyLazy(() => { - let guildId = ""; - let channelId = ""; - let width = 0; - class SidebarStore extends Flux.PersistedStore { + const current = { + guildId: "", + channelId: "", + width: 0 + }; + + let previous = { ...current }; + + class SidebarStore extends (FluxWP as IFlux).PersistedStore { static persistKey = "SidebarStore"; + // @ts-ignore - initialize(previous: { guildId?: string; channelId?: string; width?: number; } | undefined) { - if (!settings.store.persistSidebar || !previous) return; - const { guildId: prevGId, channelId: prevCId, width: prevWidth } = previous; - guildId = prevGId || ""; - channelId = prevCId || ""; - width = prevWidth || 0; + initialize(previousState: { guildId?: string; channelId?: string; width?: number; } | undefined) { + if (!settings.store.persistSidebar || !previousState) return; + const { guildId, channelId, width } = previousState; + current.guildId = guildId || ""; + current.channelId = channelId || ""; + current.width = width || 0; } getState() { - return { - guildId, - channelId, - width - }; - } - - getFullState() { - return { - guild: GuildStore.getGuild(guildId), - channel: ChannelStore.getChannel(channelId), - width - }; + return current; } } const store = new SidebarStore(FluxDispatcher, { // @ts-ignore - async NEW_SIDEBAR_CHAT({ isUser, guildId: newGId, id }: SidebarData) { - guildId = newGId || ""; + async VC_SIDEBAR_CHAT_NEW({ guildId: newGId, id }: { guildId: string | null; id: string; }) { + previous = { ...current }; - if (!isUser) { - channelId = id; + current.guildId = newGId || ""; + + if (current.guildId) { + current.channelId = id; + store.emitChange(); return; } - channelId = await ChannelActionCreators.getOrEnsurePrivateChannel(id); + current.channelId = await ChannelActionCreators.getOrEnsurePrivateChannel(id); store.emitChange(); }, - CLOSE_SIDEBAR_CHAT() { - guildId = ""; - channelId = ""; + VC_SIDEBAR_CHAT_PREVIOUS() { + if (previous.channelId) { + current.guildId = previous.guildId; + current.channelId = previous.channelId; + } store.emitChange(); }, - /* SIDEBAR_CHAT_WIDTH({ newWidth }: { newWidth: number; }) { - width = newWidth; + VC_SIDEBAR_CHAT_CLOSE() { + previous = { ...current }; + current.guildId = ""; + current.channelId = ""; store.emitChange(); - }*/ + }, }); return store; diff --git a/src/equicordplugins/sidebarChat/styles.css b/src/equicordplugins/sidebarChat/styles.css index f6e608bfb..17b891b23 100644 --- a/src/equicordplugins/sidebarChat/styles.css +++ b/src/equicordplugins/sidebarChat/styles.css @@ -5,3 +5,7 @@ line-clamp: 1; white-space: unset; } + +[class^="page_"] > * { + min-width: 0; +} \ No newline at end of file diff --git a/src/equicordplugins/voiceButtons/styles.css b/src/equicordplugins/voiceButtons/styles.css index 6fe3882df..716c9fbbe 100644 --- a/src/equicordplugins/voiceButtons/styles.css +++ b/src/equicordplugins/voiceButtons/styles.css @@ -2,6 +2,10 @@ flex: none !important; } +.voice-user-button-container { + padding-right: 4px; +} + div[class^='list_'][class*='collapsed_'] .voice-user-buttons { display: none; } \ No newline at end of file