feat(PlatformIndicators): add indicator to messages (#343)

This commit is contained in:
ActuallyTheSun 2022-12-21 21:16:32 +02:00 committed by GitHub
parent 1f73cfa91a
commit d806be1346
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 321 additions and 75 deletions

View file

@ -66,11 +66,20 @@ export default definePlugin({
/* Patch the badge list component on user profiles */
{
find: "Messages.PROFILE_USER_BADGES,role:",
replacement: {
match: /src:(\w{1,3})\[(\w{1,3})\.key\],/,
// <img src={badge.image ?? imageMap[badge.key]} {...badge.props} />
replace: (_, imageMap, badge) => `src: ${badge}.image ?? ${imageMap}[${badge}.key], ...${badge}.props,`
}
replacement: [
{
match: /src:(\w{1,3})\[(\w{1,3})\.key\],/,
// <img src={badge.image ?? imageMap[badge.key]} {...badge.props} />
replace: (_, imageMap, badge) => `src: ${badge}.image ?? ${imageMap}[${badge}.key], ...${badge}.props,`
},
{
match: /spacing:(\d{1,2}),children:(.{1,40}(.{1,2})\.jsx.+(.{1,2})\.onClick.+\)})},/,
// if the badge provides it's own component, render that instead of an image
// the badge also includes info about the user that has it (type BadgeUserArgs), which is why it's passed as props
replace: (_, s, origBadgeComponent, React, badge) =>
`spacing:${s},children:${badge}.component ? () => (0,${React}.jsx)(${badge}.component, { ...${badge} }) : ${origBadgeComponent}},`
}
]
}
],

View file

@ -0,0 +1,42 @@
/*
* 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 { Devs } from "@utils/constants";
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],
patches: [
{
find: "lostPermissionTooltipText,",
replacement: {
match: /Fragment,{children:\[(.{30,80})\]/,
replace: "Fragment,{children:Vencord.Api.MemberListDecorators.__addDecoratorsToList(this.props).concat($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)"
}
}
],
});

View file

@ -0,0 +1,35 @@
/*
* 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 { Devs } from "@utils/constants";
import definePlugin from "@utils/types";
export default definePlugin({
name: "MessageDecorationsAPI",
description: "API to add decorations to messages",
authors: [Devs.TheSun],
patches: [
{
find: ".withMentionPrefix",
replacement: {
match: /(\(\).roleDot.{10,50}{children:.{1,2})}\)/,
replace: "$1.concat(Vencord.Api.MessageDecorations.__addDecorationsToMessage(arguments[0]))})"
}
}
],
});

View file

@ -16,6 +16,9 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { addBadge, BadgePosition, ProfileBadge, removeBadge } from "@api/Badges";
import { addDecorator, removeDecorator } from "@api/MemberListDecorators";
import { addDecoration, removeDecoration } from "@api/MessageDecorations";
import { Settings } from "@api/settings";
import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants";
@ -59,10 +62,12 @@ const PlatformIcon = ({ platform, status }: { platform: Platform, status: string
return <Icon color={`var(--${getStatusColor(status)}`} tooltip={tooltip} />;
};
const getStatus = (id: string): Record<Platform, string> => PresenceStore.getState()?.clientStatuses?.[id];
const PlatformIndicator = ({ user }: { user: User; }) => {
if (!user || user.bot) return null;
const status = PresenceStore.getState()?.clientStatuses?.[user.id] as Record<Platform, string>;
const status = getStatus(user.id);
if (!status) return null;
const icons = Object.entries(status).map(([platform, status]) => (
@ -75,79 +80,95 @@ const PlatformIndicator = ({ user }: { user: User; }) => {
if (!icons.length) return null;
return (
<div
const indicator =
<span
className="vc-platform-indicator"
style={{
display: "flex", alignItems: "center", marginLeft: "4px", gap: "4px"
}}
style={{ marginLeft: "4px", gap: "4px" }}
>
{icons}
</div>
);
</span>;
return indicator;
};
const badge: ProfileBadge = {
component: PlatformIndicator,
position: BadgePosition.START,
shouldShow: userInfo => !!Object.keys(getStatus(userInfo.user.id) ?? {}).length,
key: "indicator"
};
const indicatorLocations = {
list: {
description: "In the member list",
onEnable: () => addDecorator("platform-indicator", props =>
<ErrorBoundary noop>
<PlatformIndicator user={props.user} />
</ErrorBoundary>
),
onDisable: () => removeDecorator("platform-indicator")
},
badges: {
description: "In user profiles, as badges",
onEnable: () => addBadge(badge),
onDisable: () => removeBadge(badge)
},
messages: {
description: "Inside messages",
onEnable: () => addDecoration("platform-indicator", props =>
<ErrorBoundary noop>
<PlatformIndicator user={
props.decorations[1]?.find(i => i.key === "new-member")?.props.message?.author
} />
</ErrorBoundary>
),
onDisable: () => removeDecoration("platform-indicator")
}
};
export default definePlugin({
name: "PlatformIndicators",
description: "Adds platform indicators (Desktop, Mobile, Web...) to users",
authors: [Devs.kemo],
authors: [Devs.kemo, Devs.TheSun],
dependencies: ["MessageDecorationsAPI", "MemberListDecoratorsAPI"],
patches: [
{
// Server member list decorators
find: "this.renderPremium()",
predicate: () => ["both", "list"].includes(Settings.plugins.PlatformIndicators.displayMode),
replacement: {
match: /this.renderPremium\(\)[^\]]*?\]/,
replace: "$&.concat(Vencord.Plugins.plugins.PlatformIndicators.renderPlatformIndicators(this.props))"
}
},
{
// Dm list decorators
find: "PrivateChannel.renderAvatar",
predicate: () => ["both", "list"].includes(Settings.plugins.PlatformIndicators.displayMode),
replacement: {
match: /(subText:(.{1,3})\..+?decorators:)(.+?:null)/,
replace: "$1[$3].concat(Vencord.Plugins.plugins.PlatformIndicators.renderPlatformIndicators($2.props))"
}
},
{
// User badges
find: "Messages.PROFILE_USER_BADGES",
predicate: () => ["both", "badges"].includes(Settings.plugins.PlatformIndicators.displayMode),
replacement: {
match: /(Messages\.PROFILE_USER_BADGES,role:"group",children:)(.+?\.key\)\}\)\))/,
replace: "$1[Vencord.Plugins.plugins.PlatformIndicators.renderPlatformIndicators(e)].concat($2)"
start() {
const settings = Settings.plugins.PlatformIndicators;
const { displayMode } = settings;
// transfer settings from the old ones, which had a select menu instead of booleans
if (displayMode) {
if (displayMode !== "both") settings[displayMode] = true;
else {
settings.list = true;
settings.badges = true;
}
settings.messages = true;
delete settings.displayMode;
}
],
renderPlatformIndicators: ({ user }: { user: User; }) => (
<ErrorBoundary noop>
<PlatformIndicator user={user} />
</ErrorBoundary>
),
Object.entries(indicatorLocations).forEach(([key, value]) => {
if (settings[key]) value.onEnable();
});
},
stop() {
Object.entries(indicatorLocations).forEach(([_, value]) => {
value.onDisable();
});
},
options: {
displayMode: {
type: OptionType.SELECT,
description: "Where to display the platform indicators",
restartNeeded: true,
options: [
{
label: "Member List & Badges",
value: "both",
default: true
},
{
label: "Member List Only",
value: "list"
},
{
label: "Badges Only",
value: "badges"
}
]
},
...Object.fromEntries(
Object.entries(indicatorLocations).map(([key, value]) => {
return [key, {
type: OptionType.BOOLEAN,
description: `Show indicators ${value.description.toLowerCase()}`,
// onChange doesn't give any way to know which setting was changed, so restart required
restartNeeded: true,
default: false
}];
})
)
}
});