From 189b488027a83aa0b5b5136d18c261cb205bb385 Mon Sep 17 00:00:00 2001 From: Keiran Date: Fri, 26 Dec 2025 16:45:37 +0000 Subject: [PATCH] feat(iRememberYou): Give UIDs and GIDs interactivity (#601) * feat(iRememberYou): implement complete interactivity for UIDs and GIDs - add lmb copy functionality for uids with toast - add rmb context menu for uids and group headers - add invite link storage and permission aware collection for groups * fix: update username in EquicordDevs array --- .../iRememberYou/components/data.tsx | 51 +++++++++++++++++-- .../iRememberYou/components/ui.tsx | 40 ++++++++++++--- src/equicordplugins/iRememberYou/index.tsx | 2 +- src/equicordplugins/iRememberYou/styles.css | 5 ++ src/plugins/messageClickActions/index.ts | 2 +- src/utils/constants.ts | 2 +- 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/src/equicordplugins/iRememberYou/components/data.tsx b/src/equicordplugins/iRememberYou/components/data.tsx index a00e09630..1b8733934 100644 --- a/src/equicordplugins/iRememberYou/components/data.tsx +++ b/src/equicordplugins/iRememberYou/components/data.tsx @@ -6,7 +6,7 @@ import { DataStore } from "@api/index"; import { Guild, User } from "@vencord/discord-types"; -import { ChannelStore, GuildMemberStore, GuildStore, MessageStore, UserStore, } from "@webpack/common"; +import { ChannelStore, GuildMemberStore, GuildStore, InviteActions, MessageStore, PermissionStore, UserStore, } from "@webpack/common"; export interface IUserExtra { isOwner?: boolean; @@ -25,6 +25,7 @@ export interface GroupData { id: string; users: { [key: string]: IStorageUser; }; name: string; + inviteLink?: string; } export class Data { @@ -78,6 +79,8 @@ export class Data { array: { user: User; source?: Guild; extra?: IUserExtra; }[] ) { const target = this.usersCollection; + const processedGuilds = new Set(); + for (const { user, source, extra } of array) { if (user.bot) { continue; @@ -87,7 +90,8 @@ export class Data { const group = (target[groupKey] ||= { name: source?.name || "dm", id: source?.id || user.id, - users: {} + users: {}, + inviteLink: undefined }); const usersField = group.users; const previouExtra = usersField[user.id]?.extra ?? {}; @@ -100,6 +104,11 @@ export class Data { extra: { ...previouExtra, ...extra }, iconURL: user.getAvatarURL(), }; + + if (source && !processedGuilds.has(source.id) && this.hasInvitePermissions(source.id)) { + this.collectInviteLink(source.id); + processedGuilds.add(source.id); + } } } @@ -138,7 +147,11 @@ export class Data { target.add({ user, source: guild, extra: { updatedAt: now } }); } - this.processUsersToCollection([...target]); +this.processUsersToCollection([...target]); + + if (guild && this.hasInvitePermissions(guild.id)) { + this.collectInviteLink(guild.id); + } } } @@ -163,6 +176,38 @@ export class Data { this.processUsersToCollection([...target]); } + hasInvitePermissions(guildId: string): boolean { + const guild = GuildStore.getGuild(guildId); + if (!guild) return false; + + const currentUser = UserStore.getCurrentUser(); + if (!currentUser) return false; + + if (guild.ownerId === currentUser.id) return true; + + const member = GuildMemberStore.getMember(guildId, currentUser.id); + if (!member) return false; + + return PermissionStore.can(PermissionStore.CREATE_INSTANT_INVITE, guild.id); + } + + async collectInviteLink(guildId: string) { + try { + const invites = await InviteActions.getInvites(guildId); + if (invites && invites.length > 0) { + const invite = invites[0]; + const inviteCode = invite.code; + const inviteLink = `https://discord.gg/${inviteCode}`; + + if (this.usersCollection[guildId]) { + this.usersCollection[guildId].inviteLink = inviteLink; + } + } + } catch (error) { + console.warn("Failed to collect invite link for guild:", guildId, error); + } + } + storageAutoSaveProtocol() { this._storageAutoSaveProtocol_interval = setInterval( this.updateStorage.bind(this), diff --git a/src/equicordplugins/iRememberYou/components/ui.tsx b/src/equicordplugins/iRememberYou/components/ui.tsx index f75147cd8..ba79e6da6 100644 --- a/src/equicordplugins/iRememberYou/components/ui.tsx +++ b/src/equicordplugins/iRememberYou/components/ui.tsx @@ -10,8 +10,8 @@ import { HeadingPrimary } from "@components/Heading"; import { Paragraph } from "@components/Paragraph"; import { SettingsTab, wrapTab } from "@components/settings"; import { classNameFactory } from "@utils/css"; -import { openUserProfile } from "@utils/discord"; -import { Avatar, Clickable, React, TextInput, Tooltip, } from "@webpack/common"; +import { copyWithToast, openUserProfile } from "@utils/discord"; +import { Avatar, Clickable, ContextMenuApi, Menu, React, TextInput, Tooltip, UserStore } from "@webpack/common"; import { Data, IStorageUser } from "./data"; @@ -66,10 +66,38 @@ function UserRow({ user, allowOwner = true }: { user: IStorageUser, allowOwner?: } - - - {user.id} - + { + e.preventDefault(); + const userObj = UserStore.getUser(user.id); + if (userObj) { + ContextMenuApi.openContextMenu(e, () => ( + + copyWithToast(user.id, "User ID copied to clipboard")} + /> + openUserProfile(user.id)} + /> + + )); + } + }} + > + copyWithToast(user.id, "User ID copied to clipboard")}> + + {user.id} + + diff --git a/src/equicordplugins/iRememberYou/index.tsx b/src/equicordplugins/iRememberYou/index.tsx index 02851a6e5..bc49810a8 100644 --- a/src/equicordplugins/iRememberYou/index.tsx +++ b/src/equicordplugins/iRememberYou/index.tsx @@ -18,7 +18,7 @@ import DataUI from "./components/ui"; export default definePlugin({ name: "IRememberYou", description: "Locally saves everyone you've been communicating with (including servers), in case of lose", - authors: [EquicordDevs.zoodogood], + authors: [EquicordDevs.zoodogood, EquicordDevs.keyages], dependencies: ["MessageEventsAPI"], patches: [], diff --git a/src/equicordplugins/iRememberYou/styles.css b/src/equicordplugins/iRememberYou/styles.css index cf29f97fb..3a0b283ba 100644 --- a/src/equicordplugins/iRememberYou/styles.css +++ b/src/equicordplugins/iRememberYou/styles.css @@ -51,6 +51,11 @@ align-self: flex-start; opacity: 0.75; height: min-content; + cursor: pointer; +} + +.vc-i-remember-you-user-id:hover { + opacity: 1; } .vc-i-remember-you-user { diff --git a/src/plugins/messageClickActions/index.ts b/src/plugins/messageClickActions/index.ts index 65dd23a05..cbddf4d2d 100644 --- a/src/plugins/messageClickActions/index.ts +++ b/src/plugins/messageClickActions/index.ts @@ -91,7 +91,7 @@ async function react(channelId: string, messageId: string, emoji: string) { export default definePlugin({ name: "MessageClickActions", description: "Hold Backspace and click to delete, double click to edit/reply, triple click to react", - authors: [Devs.Ven, EquicordDevs.keircn], + authors: [Devs.Ven, EquicordDevs.keyages], settings, diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 68cea8ddd..9e9347cfb 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -1221,7 +1221,7 @@ export const EquicordDevs = Object.freeze({ name: "Benjii", id: 463702169443368970n }, - keircn: { + keyages: { name: "Key", id: 1230319937155760131n },