Plugin

FriendsSince

Shows when you became friends with someone in the user popout

Friends
index.tsx
Download

Source

src/plugins/friendsSince/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 ErrorBoundary from "@components/ErrorBoundary";
10import { Devs } from "@utils/constants";
11import { getCurrentChannel } from "@utils/discord";
12import definePlugin from "@utils/types";
13import { findByCodeLazy, findByPropsLazy, findComponentByCodeLazy, findCssClassesLazy } from "@webpack";
14import { RelationshipStore, Text } from "@webpack/common";
15
16const WrapperClasses = findCssClassesLazy("memberSinceWrapper");
17const ContainerClasses = findCssClassesLazy("memberSince");
18const getCreatedAtDate = findByCodeLazy('month:"short",day:"numeric"');
19const locale = findByPropsLazy("getLocale");
20const Section = findComponentByCodeLazy("headingVariant:", '"section"', "headingIcon:");
21
22export default definePlugin({
23 name: "FriendsSince",
24 description: "Shows when you became friends with someone in the user popout",
25 tags: ["Friends"],
26 authors: [Devs.Elvyra, Devs.Antti],
27 patches: [
28 // DM User Sidebar
29 {
30 find: "#{intl::PROVISIONAL_ACCOUNT}),headingIcon:",
31 replacement: {
32 match: /#{intl::USER_PROFILE_MEMBER_SINCE}\),.{0,100}userId:(\i\.id)}\)}\)/,
33 replace: "$&,$self.FriendsSinceComponent({userId:$1,isSidebar:true})"
34 }
35 },
36 // User Profile Modal
37 {
38 find: ",applicationRoleConnection:",
39 replacement: {
40 match: /#{intl::USER_PROFILE_MEMBER_SINCE}\),.{0,100}userId:(\i\.id),.{0,100}}\)}\),/,
41 replace: "$&,$self.FriendsSinceComponent({userId:$1,isSidebar:false}),"
42 }
43 },
44 // User Profile Modal v2
45 {
46 find: ".MODAL_V2,onClose:",
47 replacement: {
48 match: /#{intl::USER_PROFILE_MEMBER_SINCE}\),.{0,100}userId:(\i\.id),.{0,100}}\)}\),/,
49 replace: "$&,$self.FriendsSinceComponent({userId:$1,isSidebar:false}),"
50 }
51 }
52 ],
53
54 FriendsSinceComponent: ErrorBoundary.wrap(({ userId, isSidebar }: { userId: string; isSidebar: boolean; }) => {
55 if (!RelationshipStore.isFriend(userId)) return null;
56
57 const friendsSince = RelationshipStore.getSince(userId);
58 if (!friendsSince) return null;
59
60 if (isSidebar) {
61 return (
62 <Section
63 heading="Friends Since"
64 headingVariant="text-xs/semibold"
65 headingColor="text-strong"
66 >
67 <Text variant="text-sm/normal">
68 {getCreatedAtDate(friendsSince, locale.getLocale())}
69 </Text>
70 </Section>
71 );
72 }
73
74 return (
75 <Section
76 heading="Friends Since"
77 headingVariant="text-xs/medium"
78 headingColor="text-default"
79 className="vc-friendsSince-profile-section"
80 >
81 <div className={WrapperClasses.memberSinceWrapper}>
82 <div className={ContainerClasses.memberSince}>
83 {!!getCurrentChannel()?.guild_id && (
84 <svg
85 aria-hidden="true"
86 width="16"
87 height="16"
88 viewBox="0 0 24 24"
89 fill="var(--interactive-icon-default)"
90 >
91 <path d="M13 10a4 4 0 1 0 0-8 4 4 0 0 0 0 8Z" />
92 <path d="M3 5v-.75C3 3.56 3.56 3 4.25 3s1.24.56 1.33 1.25C6.12 8.65 9.46 12 13 12h1a8 8 0 0 1 8 8 2 2 0 0 1-2 2 .21.21 0 0 1-.2-.15 7.65 7.65 0 0 0-1.32-2.3c-.15-.2-.42-.06-.39.17l.25 2c.02.15-.1.28-.25.28H9a2 2 0 0 1-2-2v-2.22c0-1.57-.67-3.05-1.53-4.37A15.85 15.85 0 0 1 3 5Z" />
93 </svg>
94 )}
95 <Text variant="text-sm/normal">
96 {getCreatedAtDate(friendsSince, locale.getLocale())}
97 </Text>
98 </div>
99 </div>
100 </Section>
101 );
102 }, { noop: true }),
103});
104