Plugin
ShowConnections
Show connected accounts in user popouts
1
import "./styles.css";2
3
import { isPluginEnabled } from "@api/PluginManager";4
import { definePluginSettings } from "@api/Settings";5
import ErrorBoundary from "@components/ErrorBoundary";6
import { Flex } from "@components/Flex";7
import { CopyIcon, LinkIcon } from "@components/Icons";8
import OpenInAppPlugin from "@plugins/openInApp";9
import { Devs } from "@utils/constants";10
import { copyWithToast } from "@utils/discord";11
import definePlugin, { OptionType } from "@utils/types";12
import { ConnectedAccount, User } from "@vencord/discord-types";13
import { findByCodeLazy, findByPropsLazy } from "@webpack";14
import { Tooltip, UserProfileStore } from "@webpack/common";15
16
import { VerifiedIcon } from "./VerifiedIcon";17
18
const useLegacyPlatformType: (platform: string) => string = findByCodeLazy(".TWITTER_LEGACY:");19
const platforms: { get(type: string): ConnectionPlatform; } = findByPropsLazy("isSupported", "getByUrl");20
const getProfileThemeProps = findByCodeLazy(".getPreviewThemeColors", "primaryColor:");21
22
const enum Spacing {23
COMPACT,24
COZY,25
ROOMY26
}27
const getSpacingPx = (spacing: Spacing | undefined) => (spacing ?? Spacing.COMPACT) * 2 + 4;28
29
const settings = definePluginSettings({30
iconSize: {31
type: OptionType.NUMBER,32
description: "Icon size (px)",33
default: 3234
},35
iconSpacing: {36
type: OptionType.SELECT,37
description: "Icon margin",38
default: Spacing.COZY,39
options: [40
{ label: "Compact", value: Spacing.COMPACT },41
{ label: "Cozy", value: Spacing.COZY }, class="ts-cmt">// US Spelling :/42
{ label: "Roomy", value: Spacing.ROOMY }43
]44
}45
});46
47
interface ConnectionPlatform {48
getPlatformUserUrl(connection: ConnectedAccount): string;49
icon: { lightSVG: string, darkSVG: string; };50
}51
52
const profilePopoutComponent = ErrorBoundary.wrap(53
(props: { user: User; displayProfile?: any; }) => (54
<ConnectionsComponent55
{...props}56
id={props.user.id}57
theme={getProfileThemeProps(props).theme}58
/>59
),60
{ noop: true }61
);62
63
function ConnectionsComponent({ id, theme }: { id: string, theme: string; }) {64
const profile = UserProfileStore.getUserProfile(id);65
if (!profile)66
return null;67
68
const connections = profile.connectedAccounts;69
if (!connections?.length)70
return null;71
72
return (73
<Flex gap={getSpacingPx(settings.store.iconSpacing)} flexWrap="wrap">74
{connections.map(connection => <CompactConnectionComponent connection={connection} theme={theme} key={connection.id} />)}75
</Flex>76
);77
}78
79
function CompactConnectionComponent({ connection, theme }: { connection: ConnectedAccount, theme: string; }) {80
const platform = platforms.get(useLegacyPlatformType(connection.type));81
const url = platform.getPlatformUserUrl?.(connection);82
83
const img = (84
<img85
aria-label={connection.name}86
src={theme === "light" ? platform.icon.lightSVG : platform.icon.darkSVG}87
style={{88
width: settings.store.iconSize,89
height: settings.store.iconSize90
}}91
/>92
);93
94
const TooltipIcon = url ? LinkIcon : CopyIcon;95
96
return (97
<Tooltip98
text={99
<span className="vc-sc-tooltip">100
<span className="vc-sc-connection-name">{connection.name}</span>101
{connection.verified && <VerifiedIcon />}102
<TooltipIcon height={16} width={16} className="vc-sc-tooltip-icon" />103
</span>104
}105
key={connection.id}106
>107
{tooltipProps =>108
url109
? <a110
{...tooltipProps}111
className="vc-user-connection"112
href={url}113
target="_blank"114
rel="noreferrer"115
onClick={e => {116
if (isPluginEnabled(OpenInAppPlugin.name)) {117
// handleLink will .preventDefault() if applicable118
OpenInAppPlugin.handleLink(e.currentTarget, e);119
}120
}}121
>122
{img}123
</a>124
: <button125
{...tooltipProps}126
className="vc-user-connection"127
onClick={() => copyWithToast(connection.name)}128
>129
{img}130
</button>131
132
}133
</Tooltip>134
);135
}136
137
export default definePlugin({138
name: "ShowConnections",139
description: "Show connected accounts in user popouts",140
tags: ["Friends", "Appearance"],141
authors: [Devs.TheKodeToad],142
settings,143
144
patches: [145
{146
// Same find as ReviewDB147
find: 039;"UserProfilePopout");039;,148
replacement: {149
match: /userId:\i\.id,guild:\i\}\)(?=])/,150
replace: "$&,$self.profilePopoutComponent(arguments[0])"151
}152
}153
],154
155
profilePopoutComponent,156
});157