Merge branch 'main' of https://github.com/Vendicated/Vencord
This commit is contained in:
commit
9d67421b34
|
@ -48,7 +48,7 @@ export default definePlugin({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: ".METRICS,",
|
find: ".METRICS",
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /this\._intervalId=/,
|
match: /this\._intervalId=/,
|
||||||
|
|
7
src/plugins/accountPanelServerProfile/README.md
Normal file
7
src/plugins/accountPanelServerProfile/README.md
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# AccountPanelServerProfile
|
||||||
|
|
||||||
|
Right click your account panel in the bottom left to view your profile in the current server
|
||||||
|
|
||||||
|
![](https://github.com/user-attachments/assets/3228497d-488f-479c-93d2-a32ccdb08f0f)
|
||||||
|
|
||||||
|
![](https://github.com/user-attachments/assets/6fc45363-d95f-4810-812f-2f9fb28b41b5)
|
134
src/plugins/accountPanelServerProfile/index.tsx
Normal file
134
src/plugins/accountPanelServerProfile/index.tsx
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
/*
|
||||||
|
* Vencord, a Discord client mod
|
||||||
|
* Copyright (c) 2024 Vendicated and contributors
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { definePluginSettings } from "@api/Settings";
|
||||||
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
|
import { Devs } from "@utils/constants";
|
||||||
|
import { getCurrentChannel } from "@utils/discord";
|
||||||
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
|
import { findByPropsLazy, findComponentByCodeLazy } from "@webpack";
|
||||||
|
import { ContextMenuApi, Menu, useEffect, useRef } from "@webpack/common";
|
||||||
|
import { User } from "discord-types/general";
|
||||||
|
|
||||||
|
interface UserProfileProps {
|
||||||
|
popoutProps: Record<string, any>;
|
||||||
|
currentUser: User;
|
||||||
|
originalPopout: () => React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const UserProfile = findComponentByCodeLazy("UserProfilePopoutWrapper: user cannot be undefined");
|
||||||
|
const styles = findByPropsLazy("accountProfilePopoutWrapper");
|
||||||
|
|
||||||
|
let openAlternatePopout = false;
|
||||||
|
let accountPanelRef: React.MutableRefObject<Record<PropertyKey, any> | null> = { current: null };
|
||||||
|
|
||||||
|
const AccountPanelContextMenu = ErrorBoundary.wrap(() => {
|
||||||
|
const { prioritizeServerProfile } = settings.use(["prioritizeServerProfile"]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Menu.Menu
|
||||||
|
navId="vc-ap-server-profile"
|
||||||
|
onClose={ContextMenuApi.closeContextMenu}
|
||||||
|
>
|
||||||
|
<Menu.MenuItem
|
||||||
|
id="vc-ap-view-alternate-popout"
|
||||||
|
label={prioritizeServerProfile ? "View Account Profile" : "View Server Profile"}
|
||||||
|
disabled={getCurrentChannel()?.getGuildId() == null}
|
||||||
|
action={e => {
|
||||||
|
openAlternatePopout = true;
|
||||||
|
accountPanelRef.current?.props.onMouseDown();
|
||||||
|
accountPanelRef.current?.props.onClick(e);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Menu.MenuCheckboxItem
|
||||||
|
id="vc-ap-prioritize-server-profile"
|
||||||
|
label="Prioritize Server Profile"
|
||||||
|
checked={prioritizeServerProfile}
|
||||||
|
action={() => settings.store.prioritizeServerProfile = !prioritizeServerProfile}
|
||||||
|
/>
|
||||||
|
</Menu.Menu>
|
||||||
|
);
|
||||||
|
}, { noop: true });
|
||||||
|
|
||||||
|
const settings = definePluginSettings({
|
||||||
|
prioritizeServerProfile: {
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
description: "Prioritize Server Profile when left clicking your account panel",
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default definePlugin({
|
||||||
|
name: "AccountPanelServerProfile",
|
||||||
|
description: "Right click your account panel in the bottom left to view your profile in the current server",
|
||||||
|
authors: [Devs.Nuckyz, Devs.relitrix],
|
||||||
|
settings,
|
||||||
|
|
||||||
|
patches: [
|
||||||
|
{
|
||||||
|
find: ".Messages.ACCOUNT_SPEAKING_WHILE_MUTED",
|
||||||
|
group: true,
|
||||||
|
replacement: [
|
||||||
|
{
|
||||||
|
match: /(?<=\.SIZE_32\)}\);)/,
|
||||||
|
replace: "$self.useAccountPanelRef();"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
match: /(\.AVATAR,children:.+?renderPopout:(\i)=>){(.+?)}(?=,position)(?<=currentUser:(\i).+?)/,
|
||||||
|
replace: (_, rest, popoutProps, originalPopout, currentUser) => `${rest}$self.UserProfile({popoutProps:${popoutProps},currentUser:${currentUser},originalPopout:()=>{${originalPopout}}})`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
match: /\.AVATAR,children:.+?(?=renderPopout:)/,
|
||||||
|
replace: "$&onRequestClose:$self.onPopoutClose,"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
match: /(?<=.avatarWrapper,)/,
|
||||||
|
replace: "ref:$self.accountPanelRef,onContextMenu:$self.openAccountPanelContextMenu,"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
get accountPanelRef() {
|
||||||
|
return accountPanelRef;
|
||||||
|
},
|
||||||
|
|
||||||
|
useAccountPanelRef() {
|
||||||
|
useEffect(() => () => {
|
||||||
|
accountPanelRef.current = null;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (accountPanelRef = useRef(null));
|
||||||
|
},
|
||||||
|
|
||||||
|
openAccountPanelContextMenu(event: React.UIEvent) {
|
||||||
|
ContextMenuApi.openContextMenu(event, AccountPanelContextMenu);
|
||||||
|
},
|
||||||
|
|
||||||
|
onPopoutClose() {
|
||||||
|
openAlternatePopout = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
UserProfile: ErrorBoundary.wrap(({ popoutProps, currentUser, originalPopout }: UserProfileProps) => {
|
||||||
|
if (
|
||||||
|
(settings.store.prioritizeServerProfile && openAlternatePopout) ||
|
||||||
|
(!settings.store.prioritizeServerProfile && !openAlternatePopout)
|
||||||
|
) {
|
||||||
|
return originalPopout();
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentChannel = getCurrentChannel();
|
||||||
|
if (currentChannel?.getGuildId() == null) {
|
||||||
|
return originalPopout();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.accountProfilePopoutWrapper}>
|
||||||
|
<UserProfile {...popoutProps} userId={currentUser.id} guildId={currentChannel.getGuildId()} channelId={currentChannel.id} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}, { noop: true })
|
||||||
|
});
|
|
@ -26,12 +26,11 @@ import { Margins } from "@utils/margins";
|
||||||
import { classes } from "@utils/misc";
|
import { classes } from "@utils/misc";
|
||||||
import { useAwaiter } from "@utils/react";
|
import { useAwaiter } from "@utils/react";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { findByCodeLazy, findByPropsLazy, findComponentByCodeLazy } from "@webpack";
|
import { findByCodeLazy, findComponentByCodeLazy } from "@webpack";
|
||||||
import { ApplicationAssetUtils, Button, FluxDispatcher, Forms, GuildStore, React, SelectedChannelStore, SelectedGuildStore, UserStore } from "@webpack/common";
|
import { ApplicationAssetUtils, Button, FluxDispatcher, Forms, GuildStore, React, SelectedChannelStore, SelectedGuildStore, UserStore } from "@webpack/common";
|
||||||
|
|
||||||
const useProfileThemeStyle = findByCodeLazy("profileThemeStyle:", "--profile-gradient-primary-color");
|
const useProfileThemeStyle = findByCodeLazy("profileThemeStyle:", "--profile-gradient-primary-color");
|
||||||
const ActivityComponent = findComponentByCodeLazy("onOpenGameProfile");
|
const ActivityComponent = findComponentByCodeLazy("onOpenGameProfile");
|
||||||
const ActivityClassName = findByPropsLazy("activity", "buttonColor");
|
|
||||||
|
|
||||||
const ShowCurrentGame = getUserSettingLazy<boolean>("status", "showCurrentGame")!;
|
const ShowCurrentGame = getUserSettingLazy<boolean>("status", "showCurrentGame")!;
|
||||||
|
|
||||||
|
@ -436,8 +435,8 @@ export default definePlugin({
|
||||||
|
|
||||||
<Forms.FormDivider className={Margins.top8} />
|
<Forms.FormDivider className={Margins.top8} />
|
||||||
|
|
||||||
<div style={{ width: "284px", ...profileThemeStyle }}>
|
<div style={{ width: "284px", ...profileThemeStyle, padding: 8, marginTop: 8, borderRadius: 8, background: "var(--bg-mod-faint)" }}>
|
||||||
{activity[0] && <ActivityComponent activity={activity[0]} className={ActivityClassName.activity} channelId={SelectedChannelStore.getChannelId()}
|
{activity[0] && <ActivityComponent activity={activity[0]} channelId={SelectedChannelStore.getChannelId()}
|
||||||
guild={GuildStore.getGuild(SelectedGuildStore.getLastSelectedGuildId())}
|
guild={GuildStore.getGuild(SelectedGuildStore.getLastSelectedGuildId())}
|
||||||
application={{ id: settings.store.appID }}
|
application={{ id: settings.store.appID }}
|
||||||
user={UserStore.getCurrentUser()} />}
|
user={UserStore.getCurrentUser()} />}
|
||||||
|
|
|
@ -266,7 +266,7 @@ export default definePlugin({
|
||||||
replace: (m, props, nowPlaying) => `${m}$self.renderToggleGameActivityButton(${props},${nowPlaying}),`
|
replace: (m, props, nowPlaying) => `${m}$self.renderToggleGameActivityButton(${props},${nowPlaying}),`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// Discord has 3 different components for activities. Currently, the last is the one being used
|
// Discord has 2 different components for activities. Currently, the last is the one being used
|
||||||
{
|
{
|
||||||
find: ".activityTitleText,variant",
|
find: ".activityTitleText,variant",
|
||||||
replacement: {
|
replacement: {
|
||||||
|
@ -274,13 +274,6 @@ export default definePlugin({
|
||||||
replace: (m, props) => `${m}$self.renderToggleActivityButton(${props}),`
|
replace: (m, props) => `${m}$self.renderToggleActivityButton(${props}),`
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
find: ".activityCardDetails,children",
|
|
||||||
replacement: {
|
|
||||||
match: /\.activityCardDetails.+?children:(\i\.application)\.name.*?}\),/,
|
|
||||||
replace: (m, props) => `${m}$self.renderToggleActivityButton(${props}),`
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
find: ".promotedLabelWrapperNonBanner,children",
|
find: ".promotedLabelWrapperNonBanner,children",
|
||||||
replacement: {
|
replacement: {
|
||||||
|
|
|
@ -66,14 +66,14 @@ export function addPatch(newPatch: Omit<Patch, "plugin">, pluginName: string) {
|
||||||
patch.replacement = [patch.replacement];
|
patch.replacement = [patch.replacement];
|
||||||
}
|
}
|
||||||
|
|
||||||
patch.replacement = patch.replacement.filter(({ predicate }) => !predicate || predicate());
|
|
||||||
|
|
||||||
if (IS_REPORTER) {
|
if (IS_REPORTER) {
|
||||||
patch.replacement.forEach(r => {
|
patch.replacement.forEach(r => {
|
||||||
delete r.predicate;
|
delete r.predicate;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
patch.replacement = patch.replacement.filter(({ predicate }) => !predicate || predicate());
|
||||||
|
|
||||||
patches.push(patch);
|
patches.push(patch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,16 +62,7 @@ export default definePlugin({
|
||||||
replace: "return 0;"
|
replace: "return 0;"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// New message requests hook
|
// Message requests hook
|
||||||
{
|
|
||||||
find: 'location:"use-message-requests-count"',
|
|
||||||
predicate: () => settings.store.hideMessageRequestsCount,
|
|
||||||
replacement: {
|
|
||||||
match: /getNonChannelAckId\(\i\.\i\.MESSAGE_REQUESTS\).+?return /,
|
|
||||||
replace: "$&0;"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// Old message requests hook
|
|
||||||
{
|
{
|
||||||
find: "getMessageRequestsCount(){",
|
find: "getMessageRequestsCount(){",
|
||||||
predicate: () => settings.store.hideMessageRequestsCount,
|
predicate: () => settings.store.hideMessageRequestsCount,
|
||||||
|
|
|
@ -48,7 +48,7 @@ export default definePlugin({
|
||||||
find: ".Messages.FRIEND_REQUEST_CANCEL",
|
find: ".Messages.FRIEND_REQUEST_CANCEL",
|
||||||
replacement: {
|
replacement: {
|
||||||
predicate: () => settings.store.showDates,
|
predicate: () => settings.store.showDates,
|
||||||
match: /subText:(\i)(?=,className:\i\.userInfo}\))(?<=user:(\i).+?)/,
|
match: /subText:(\i)(?<=user:(\i).+?)/,
|
||||||
replace: (_, subtext, user) => `subText:$self.makeSubtext(${subtext},${user})`
|
replace: (_, subtext, user) => `subText:$self.makeSubtext(${subtext},${user})`
|
||||||
}
|
}
|
||||||
}],
|
}],
|
||||||
|
@ -66,7 +66,7 @@ export default definePlugin({
|
||||||
makeSubtext(text: string, user: User) {
|
makeSubtext(text: string, user: User) {
|
||||||
const since = this.getSince(user);
|
const since = this.getSince(user);
|
||||||
return (
|
return (
|
||||||
<Flex flexDirection="row" style={{ gap: 0, flexWrap: "wrap", lineHeight: "0.9rem" }}>
|
<Flex flexDirection="column" style={{ gap: 0, flexWrap: "wrap", lineHeight: "0.9rem" }}>
|
||||||
<span>{text}</span>
|
<span>{text}</span>
|
||||||
{!isNaN(since.getTime()) && <span>Received — {since.toDateString()}</span>}
|
{!isNaN(since.getTime()) && <span>Received — {since.toDateString()}</span>}
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
|
@ -5,9 +5,11 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { definePluginSettings } from "@api/Settings";
|
import { definePluginSettings } from "@api/Settings";
|
||||||
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { findComponentByCodeLazy } from "@webpack";
|
import { findComponentByCodeLazy } from "@webpack";
|
||||||
|
import { RequiredDeep } from "type-fest";
|
||||||
|
|
||||||
interface Activity {
|
interface Activity {
|
||||||
timestamps?: ActivityTimestamps;
|
timestamps?: ActivityTimestamps;
|
||||||
|
@ -18,7 +20,15 @@ interface ActivityTimestamps {
|
||||||
end?: string;
|
end?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ActivityTimeBar = findComponentByCodeLazy<ActivityTimestamps>(".Millis.HALF_SECOND", ".bar", ".progress");
|
interface TimebarComponentProps {
|
||||||
|
activity: Activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ActivityTimeBar = findComponentByCodeLazy<ActivityTimestamps>(".bar", ".progress", "(100*");
|
||||||
|
|
||||||
|
function isActivityTimestamped(activity: Activity): activity is RequiredDeep<Activity> {
|
||||||
|
return activity.timestamps != null && activity.timestamps.start != null && activity.timestamps.end != null;
|
||||||
|
}
|
||||||
|
|
||||||
export const settings = definePluginSettings({
|
export const settings = definePluginSettings({
|
||||||
hideActivityDetailText: {
|
hideActivityDetailText: {
|
||||||
|
@ -40,12 +50,12 @@ export default definePlugin({
|
||||||
settings,
|
settings,
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: ".Messages.USER_ACTIVITY_PLAYING",
|
find: ".gameState,children:",
|
||||||
replacement: [
|
replacement: [
|
||||||
// Insert Spotify time bar component
|
// Insert Spotify time bar component
|
||||||
{
|
{
|
||||||
match: /\(0,.{0,30}activity:(\i),className:\i\.badges\}\)/g,
|
match: /\(0,.{0,30}activity:(\i),className:\i\.badges\}\)/g,
|
||||||
replace: "$&,$self.getTimeBar($1)"
|
replace: "$&,$self.TimebarComponent({activity:$1})"
|
||||||
},
|
},
|
||||||
// Hide the large title on listening activities, to make them look more like Spotify (also visible from hovering over the large icon)
|
// Hide the large title on listening activities, to make them look more like Spotify (also visible from hovering over the large icon)
|
||||||
{
|
{
|
||||||
|
@ -64,13 +74,11 @@ export default definePlugin({
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
isActivityTimestamped(activity: Activity) {
|
isActivityTimestamped,
|
||||||
return activity.timestamps != null && activity.timestamps.start != null && activity.timestamps.end != null;
|
|
||||||
},
|
|
||||||
|
|
||||||
getTimeBar(activity: Activity) {
|
TimebarComponent: ErrorBoundary.wrap(({ activity }: TimebarComponentProps) => {
|
||||||
if (this.isActivityTimestamped(activity)) {
|
if (!isActivityTimestamped(activity)) return null;
|
||||||
return <ActivityTimeBar start={activity.timestamps!.start} end={activity.timestamps!.end} />;
|
|
||||||
}
|
return <ActivityTimeBar start={activity.timestamps.start} end={activity.timestamps.end} />;
|
||||||
}
|
}, { noop: true })
|
||||||
});
|
});
|
||||||
|
|
|
@ -39,7 +39,8 @@ export const Devs = /* #__PURE__*/ Object.freeze({
|
||||||
},
|
},
|
||||||
Arjix: {
|
Arjix: {
|
||||||
name: "ArjixWasTaken",
|
name: "ArjixWasTaken",
|
||||||
id: 674710789138939916n
|
id: 674710789138939916n,
|
||||||
|
badge: false
|
||||||
},
|
},
|
||||||
Cyn: {
|
Cyn: {
|
||||||
name: "Cynosphere",
|
name: "Cynosphere",
|
||||||
|
@ -566,6 +567,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({
|
||||||
name: "niko",
|
name: "niko",
|
||||||
id: 341377368075796483n,
|
id: 341377368075796483n,
|
||||||
},
|
},
|
||||||
|
relitrix: {
|
||||||
|
name: "Relitrix",
|
||||||
|
id: 423165393901715456n,
|
||||||
|
},
|
||||||
RamziAH: {
|
RamziAH: {
|
||||||
name: "RamziAH",
|
name: "RamziAH",
|
||||||
id: 1279957227612147747n,
|
id: 1279957227612147747n,
|
||||||
|
|
Loading…
Reference in a new issue