Plugin

ShowConnections

Show connected accounts in user popouts

Friends Appearance
index.tsx
Download

Source

src/plugins/showConnections/index.tsx
1/*
2 * FiveCord — a Discord client mod
3 * Copyright (c) 2025 FiveCord
4 * SPDX-License-Identifier: GPL-3.0-or-later
5 */
6
7import "./styles.css";
8
9import { isPluginEnabled } from "@api/PluginManager";
10import { definePluginSettings } from "@api/Settings";
11import ErrorBoundary from "@components/ErrorBoundary";
12import { Flex } from "@components/Flex";
13import { CopyIcon, LinkIcon } from "@components/Icons";
14import OpenInAppPlugin from "@plugins/openInApp";
15import { Devs } from "@utils/constants";
16import { copyWithToast } from "@utils/discord";
17import definePlugin, { OptionType } from "@utils/types";
18import { ConnectedAccount, User } from "@vencord/discord-types";
19import { findByCodeLazy, findByPropsLazy } from "@webpack";
20import { Tooltip, UserProfileStore } from "@webpack/common";
21
22import { VerifiedIcon } from "./VerifiedIcon";
23
24const useLegacyPlatformType: (platform: string) => string = findByCodeLazy(".TWITTER_LEGACY:");
25const platforms: { get(type: string): ConnectionPlatform; } = findByPropsLazy("isSupported", "getByUrl");
26const getProfileThemeProps = findByCodeLazy(".getPreviewThemeColors", "primaryColor:");
27
28const enum Spacing {
29 COMPACT,
30 COZY,
31 ROOMY
32}
33const getSpacingPx = (spacing: Spacing | undefined) => (spacing ?? Spacing.COMPACT) * 2 + 4;
34
35const settings = definePluginSettings({
36 iconSize: {
37 type: OptionType.NUMBER,
38 description: "Icon size (px)",
39 default: 32
40 },
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
53interface ConnectionPlatform {
54 getPlatformUserUrl(connection: ConnectedAccount): string;
55 icon: { lightSVG: string, darkSVG: string; };
56}
57
58const profilePopoutComponent = ErrorBoundary.wrap(
59 (props: { user: User; displayProfile?: any; }) => (
60 <ConnectionsComponent
61 {...props}
62 id={props.user.id}
63 theme={getProfileThemeProps(props).theme}
64 />
65 ),
66 { noop: true }
67);
68
69function 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
85function 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 <img
91 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.iconSize
96 }}
97 />
98 );
99
100 const TooltipIcon = url ? LinkIcon : CopyIcon;
101
102 return (
103 <Tooltip
104 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 url
115 ? <a
116 {...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 applicable
124 OpenInAppPlugin.handleLink(e.currentTarget, e);
125 }
126 }}
127 >
128 {img}
129 </a>
130 : <button
131 {...tooltipProps}
132 className="vc-user-connection"
133 onClick={() => copyWithToast(connection.name)}
134 >
135 {img}
136 </button>
137
138 }
139 </Tooltip>
140 );
141}
142
143export 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 ReviewDB
153 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