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