Compare commits
3 Commits
v1.2.9
...
componentU
Author | SHA1 | Date | |
---|---|---|---|
|
5d1d054b7e | ||
|
a11d5a9111 | ||
|
d7ac418e05 |
29
src/api/ComponentUpdater.ts
Normal file
29
src/api/ComponentUpdater.ts
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2023 Vendicated and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { proxyLazy } from "@utils/lazy";
|
||||
|
||||
const p = proxyLazy<typeof import("plugins/_api/componentUpdater").default>(() => Vencord.Plugins.plugins.ComponentUpdaterAPI as any);
|
||||
|
||||
/**
|
||||
* Rerender a specific message
|
||||
* @param messageId The id of the message to rerender
|
||||
*/
|
||||
export function updateMessageComponent(messageId: string) {
|
||||
p.forceUpdaters.get(messageId)?.();
|
||||
}
|
@ -18,6 +18,7 @@
|
||||
|
||||
import * as $Badges from "./Badges";
|
||||
import * as $Commands from "./Commands";
|
||||
import * as $ComponentUpdater from "./ComponentUpdater";
|
||||
import * as $ContextMenu from "./ContextMenu";
|
||||
import * as $DataStore from "./DataStore";
|
||||
import * as $MemberListDecorators from "./MemberListDecorators";
|
||||
@ -109,3 +110,8 @@ export const Notifications = $Notifications;
|
||||
* An api allowing you to patch and add/remove items to/from context menus
|
||||
*/
|
||||
export const ContextMenu = $ContextMenu;
|
||||
|
||||
/**
|
||||
* An api allowing you to update/rerender components
|
||||
*/
|
||||
export const ComponentUpdater = $ComponentUpdater;
|
||||
|
51
src/plugins/_api/componentUpdater.ts
Normal file
51
src/plugins/_api/componentUpdater.ts
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2023 Vendicated and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Devs } from "@utils/constants";
|
||||
import { useForceUpdater } from "@utils/react";
|
||||
import definePlugin from "@utils/types";
|
||||
import { useEffect } from "@webpack/common";
|
||||
import { Channel, Message } from "discord-types/general";
|
||||
|
||||
const forceUpdaters = new Map<string, () => void>();
|
||||
|
||||
function useUpdater(data: { channel: Channel; message: Message; }) {
|
||||
const forceUpdater = useForceUpdater();
|
||||
|
||||
useEffect(() => {
|
||||
forceUpdaters.set(data.message.id, forceUpdater);
|
||||
return () => void forceUpdaters.delete(data.message.id);
|
||||
}, [data.message.id]);
|
||||
}
|
||||
|
||||
export default definePlugin({
|
||||
name: "ComponentUpdaterAPI",
|
||||
description: "API to update / force rerender several components, such as messages",
|
||||
authors: [Devs.Ven],
|
||||
|
||||
patches: [{
|
||||
find: ".renderContentOnly;",
|
||||
replacement: {
|
||||
match: /=(\i)\.renderContentOnly;/,
|
||||
replace: "$&$self.useUpdater($1);"
|
||||
}
|
||||
}],
|
||||
|
||||
useUpdater,
|
||||
forceUpdaters
|
||||
});
|
@ -16,6 +16,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { updateMessageComponent } from "@api/ComponentUpdater";
|
||||
import { addAccessory } from "@api/MessageAccessories";
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
@ -28,7 +29,6 @@ import { find, findByCode, findByPropsLazy } from "@webpack";
|
||||
import {
|
||||
Button,
|
||||
ChannelStore,
|
||||
FluxDispatcher,
|
||||
GuildStore,
|
||||
MessageStore,
|
||||
Parser,
|
||||
@ -228,10 +228,7 @@ function MessageEmbedAccessory({ message }: { message: Message; }) {
|
||||
delete msg.interaction;
|
||||
|
||||
messageFetchQueue.push(() => fetchMessage(channelID, messageID)
|
||||
.then(m => m && FluxDispatcher.dispatch({
|
||||
type: "MESSAGE_UPDATE",
|
||||
message: msg
|
||||
}))
|
||||
.then(m => m && updateMessageComponent(message.id))
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Flex } from "@components/Flex";
|
||||
import { InfoIcon, OwnerCrownIcon } from "@components/Icons";
|
||||
import { getUniqueUsername } from "@utils/discord";
|
||||
import { ModalCloseButton, ModalContent, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import { ContextMenu, FluxDispatcher, GuildMemberStore, Menu, PermissionsBits, Text, Tooltip, useEffect, UserStore, useState, useStateFromStores } from "@webpack/common";
|
||||
import type { Guild } from "discord-types/general";
|
||||
@ -136,7 +137,7 @@ function RolesAndUsersPermissionsComponent({ permissions, guild, modalProps, hea
|
||||
permission.type === PermissionType.Role
|
||||
? role?.name || "Unknown Role"
|
||||
: permission.type === PermissionType.User
|
||||
? user?.tag || "Unknown User"
|
||||
? (user && getUniqueUsername(user)) || "Unknown User"
|
||||
: (
|
||||
<Flex style={{ gap: "0.2em", justifyItems: "center" }}>
|
||||
@owner
|
||||
|
@ -16,6 +16,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { getUniqueUsername, openUserProfile } from "@utils/discord";
|
||||
import { UserUtils } from "@webpack/common";
|
||||
|
||||
import settings from "./settings";
|
||||
@ -43,11 +44,19 @@ export async function onRelationshipRemove({ relationship: { type, id } }: Relat
|
||||
switch (type) {
|
||||
case RelationshipType.FRIEND:
|
||||
if (settings.store.friends)
|
||||
notify(`${user.tag} removed you as a friend.`, user.getAvatarURL(undefined, undefined, false));
|
||||
notify(
|
||||
`${getUniqueUsername(user)} removed you as a friend.`,
|
||||
user.getAvatarURL(undefined, undefined, false),
|
||||
() => openUserProfile(user.id)
|
||||
);
|
||||
break;
|
||||
case RelationshipType.FRIEND_REQUEST:
|
||||
if (settings.store.friendRequestCancels)
|
||||
notify(`A friend request from ${user.tag} has been removed.`, user.getAvatarURL(undefined, undefined, false));
|
||||
notify(
|
||||
`A friend request from ${getUniqueUsername(user)} has been removed.`,
|
||||
user.getAvatarURL(undefined, undefined, false),
|
||||
() => openUserProfile(user.id)
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
import { DataStore, Notices } from "@api/index";
|
||||
import { showNotification } from "@api/Notifications";
|
||||
import { getUniqueUsername, openUserProfile } from "@utils/discord";
|
||||
import { ChannelStore, GuildMemberStore, GuildStore, RelationshipStore, UserStore, UserUtils } from "@webpack/common";
|
||||
|
||||
import settings from "./settings";
|
||||
@ -69,7 +70,11 @@ export async function syncAndRunChecks() {
|
||||
|
||||
const user = await UserUtils.fetchUser(id).catch(() => void 0);
|
||||
if (user)
|
||||
notify(`You are no longer friends with ${user.tag}.`, user.getAvatarURL(undefined, undefined, false));
|
||||
notify(
|
||||
`You are no longer friends with ${getUniqueUsername(user)}.`,
|
||||
user.getAvatarURL(undefined, undefined, false),
|
||||
() => openUserProfile(user.id)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,20 +84,25 @@ export async function syncAndRunChecks() {
|
||||
|
||||
const user = await UserUtils.fetchUser(id).catch(() => void 0);
|
||||
if (user)
|
||||
notify(`Friend request from ${user.tag} has been revoked.`, user.getAvatarURL(undefined, undefined, false));
|
||||
notify(
|
||||
`Friend request from ${getUniqueUsername(user)} has been revoked.`,
|
||||
user.getAvatarURL(undefined, undefined, false),
|
||||
() => openUserProfile(user.id)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function notify(text: string, icon?: string) {
|
||||
export function notify(text: string, icon?: string, onClick?: () => void) {
|
||||
if (settings.store.notices)
|
||||
Notices.showNotice(text, "OK", () => Notices.popNotice());
|
||||
|
||||
showNotification({
|
||||
title: "Relationship Notifier",
|
||||
body: text,
|
||||
icon
|
||||
icon,
|
||||
onClick
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { openUserProfile } from "@utils/discord";
|
||||
import { classes } from "@utils/misc";
|
||||
import { LazyComponent } from "@utils/react";
|
||||
import { filters, findBulk } from "@webpack";
|
||||
@ -24,7 +25,7 @@ import { Alerts, moment, Timestamp, UserStore } from "@webpack/common";
|
||||
import { Review, ReviewType } from "../entities";
|
||||
import { deleteReview, reportReview } from "../reviewDbApi";
|
||||
import { settings } from "../settings";
|
||||
import { canDeleteReview, cl, openUserProfileModal, showToast } from "../utils";
|
||||
import { canDeleteReview, cl, showToast } from "../utils";
|
||||
import { DeleteButton, ReportButton } from "./MessageButton";
|
||||
import ReviewBadge from "./ReviewBadge";
|
||||
|
||||
@ -49,7 +50,7 @@ export default LazyComponent(() => {
|
||||
|
||||
return function ReviewComponent({ review, refetch }: { review: Review; refetch(): void; }) {
|
||||
function openModal() {
|
||||
openUserProfileModal(review.sender.discordID);
|
||||
openUserProfile(review.sender.discordID);
|
||||
}
|
||||
|
||||
function delReview() {
|
||||
|
@ -20,24 +20,13 @@ import { classNameFactory } from "@api/Styles";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import { openModal } from "@utils/modal";
|
||||
import { findByProps } from "@webpack";
|
||||
import { FluxDispatcher, React, SelectedChannelStore, Toasts, UserUtils } from "@webpack/common";
|
||||
import { React, Toasts } from "@webpack/common";
|
||||
|
||||
import { Review, UserType } from "./entities";
|
||||
import { settings } from "./settings";
|
||||
|
||||
export const cl = classNameFactory("vc-rdb-");
|
||||
|
||||
export async function openUserProfileModal(userId: string) {
|
||||
await UserUtils.fetchUser(userId);
|
||||
|
||||
await FluxDispatcher.dispatch({
|
||||
type: "USER_PROFILE_MODAL_OPEN",
|
||||
userId,
|
||||
channelId: SelectedChannelStore.getChannelId(),
|
||||
analyticsLocation: "Explosive Hotel"
|
||||
});
|
||||
}
|
||||
|
||||
export function authorize(callback?: any) {
|
||||
const { OAuth2AuthorizeModal } = findByProps("OAuth2AuthorizeModal");
|
||||
|
||||
|
@ -19,13 +19,13 @@
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { openUserProfile } from "@utils/discord";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByCodeLazy } from "@webpack";
|
||||
import { GuildMemberStore, React, RelationshipStore, SelectedChannelStore } from "@webpack/common";
|
||||
import { GuildMemberStore, React, RelationshipStore } from "@webpack/common";
|
||||
import { User } from "discord-types/general";
|
||||
|
||||
const Avatar = findByCodeLazy(".typingIndicatorRef", "svg");
|
||||
const openProfile = findByCodeLazy("friendToken", "USER_PROFILE_MODAL_OPEN");
|
||||
|
||||
const settings = definePluginSettings({
|
||||
showAvatars: {
|
||||
@ -64,15 +64,7 @@ const TypingUser = ErrorBoundary.wrap(function ({ user, guildId }: Props) {
|
||||
<strong
|
||||
role="button"
|
||||
onClick={() => {
|
||||
openProfile({
|
||||
userId: user.id,
|
||||
guildId,
|
||||
channelId: SelectedChannelStore.getChannelId(),
|
||||
analyticsLocation: {
|
||||
page: guildId ? "Guild Channel" : "DM Channel",
|
||||
section: "Profile Popout"
|
||||
}
|
||||
});
|
||||
openUserProfile(user.id);
|
||||
}}
|
||||
style={{
|
||||
display: "grid",
|
||||
|
@ -17,9 +17,9 @@
|
||||
*/
|
||||
|
||||
import { MessageObject } from "@api/MessageEvents";
|
||||
import { findByPropsLazy, findLazy } from "@webpack";
|
||||
import { ChannelStore, ComponentDispatch, GuildStore, MaskedLink, ModalImageClasses, PrivateChannelsStore, SelectedChannelStore } from "@webpack/common";
|
||||
import { Guild, Message } from "discord-types/general";
|
||||
import { findByCodeLazy, findByPropsLazy, findLazy } from "@webpack";
|
||||
import { ChannelStore, ComponentDispatch, GuildStore, MaskedLink, ModalImageClasses, PrivateChannelsStore, SelectedChannelStore, SelectedGuildStore, UserUtils } from "@webpack/common";
|
||||
import { Guild, Message, User } from "discord-types/general";
|
||||
|
||||
import { ImageModal, ModalRoot, ModalSize, openModal } from "./modal";
|
||||
|
||||
@ -99,3 +99,28 @@ export function openImageModal(url: string, props?: Partial<React.ComponentProps
|
||||
</ModalRoot>
|
||||
));
|
||||
}
|
||||
|
||||
const openProfile = findByCodeLazy("friendToken", "USER_PROFILE_MODAL_OPEN");
|
||||
|
||||
export async function openUserProfile(id: string) {
|
||||
const user = await UserUtils.fetchUser(id);
|
||||
if (!user) throw new Error("No such user: " + id);
|
||||
|
||||
const guildId = SelectedGuildStore.getGuildId();
|
||||
openProfile({
|
||||
userId: id,
|
||||
guildId,
|
||||
channelId: SelectedChannelStore.getChannelId(),
|
||||
analyticsLocation: {
|
||||
page: guildId ? "Guild Channel" : "DM Channel",
|
||||
section: "Profile Popout"
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the unique username for a user. Returns user.username for pomelo people, user.tag otherwise
|
||||
*/
|
||||
export function getUniqueUsername(user: User) {
|
||||
return user.discriminator === "0" ? user.username : user.tag;
|
||||
}
|
||||
|
9
src/webpack/common/types/stores.d.ts
vendored
9
src/webpack/common/types/stores.d.ts
vendored
@ -20,14 +20,23 @@ import { Channel } from "discord-types/general";
|
||||
|
||||
import { FluxDispatcher, FluxEvents } from "./utils";
|
||||
|
||||
type GenericFunction = (...args: any[]) => any;
|
||||
|
||||
export class FluxStore {
|
||||
constructor(dispatcher: FluxDispatcher, eventHandlers?: Partial<Record<FluxEvents, (data: any) => void>>);
|
||||
|
||||
addChangeListener(callback: () => void): void;
|
||||
addReactChangeListener(callback: () => void): void;
|
||||
removeChangeListener(callback: () => void): void;
|
||||
removeReactChangeListener(callback: () => void): void;
|
||||
emitChange(): void;
|
||||
getDispatchToken(): string;
|
||||
getName(): string;
|
||||
initialize(): void;
|
||||
initializeIfNeeded(): void;
|
||||
registerActionHandlers: GenericFunction;
|
||||
syncWith: GenericFunction;
|
||||
waitFor: GenericFunction;
|
||||
__getLocalVars(): Record<string, any>;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user