Merge branch 'feat/usercss' of ssh://github.com/lewisakura/Vencord into feat/usercss

This commit is contained in:
Lewis Crichton 2023-09-09 19:48:39 +01:00
commit f2dc34e023
No known key found for this signature in database
19 changed files with 395 additions and 46 deletions

View file

@ -38,7 +38,7 @@ jobs:
run: pnpm build --standalone run: pnpm build --standalone
- name: Generate plugin list - name: Generate plugin list
run: pnpm generatePluginJson dist/plugins.json run: pnpm generatePluginJson dist/plugins.json dist/plugin-readmes.json
- name: Clean up obsolete files - name: Clean up obsolete files
run: | run: |

View file

@ -1,7 +1,7 @@
{ {
"name": "vencord", "name": "vencord",
"private": "true", "private": "true",
"version": "1.4.6", "version": "1.4.7",
"description": "The cutest Discord client mod", "description": "The cutest Discord client mod",
"homepage": "https://github.com/Vendicated/Vencord#readme", "homepage": "https://github.com/Vendicated/Vencord#readme",
"bugs": { "bugs": {

View file

@ -165,7 +165,11 @@ async function parseFile(fileName: string) {
data.target = target as any; data.target = target as any;
} }
return data; let readme = "";
try {
readme = readFileSync(join(fileName, "..", "README.md"), "utf-8");
} catch { }
return [data, readme] as const;
} }
throw fail("no default export called 'definePlugin' found"); throw fail("no default export called 'definePlugin' found");
@ -194,18 +198,24 @@ function isPluginFile({ name }: { name: string; }) {
(async () => { (async () => {
parseDevs(); parseDevs();
const plugins = ["src/plugins", "src/plugins/_core"].flatMap(dir => const plugins = [] as PluginData[];
const readmes = {} as Record<string, string>;
await Promise.all(["src/plugins", "src/plugins/_core"].flatMap(dir =>
readdirSync(dir, { withFileTypes: true }) readdirSync(dir, { withFileTypes: true })
.filter(isPluginFile) .filter(isPluginFile)
.map(async dirent => .map(async dirent => {
parseFile(await getEntryPoint(dir, dirent)) const [data, readme] = await parseFile(await getEntryPoint(dir, dirent));
) plugins.push(data);
); if (readme) readmes[data.name] = readme;
})
));
const data = JSON.stringify(await Promise.all(plugins)); const data = JSON.stringify(plugins);
if (process.argv.length > 2) { if (process.argv.length > 3) {
writeFileSync(process.argv[2], data); writeFileSync(process.argv[2], data);
writeFileSync(process.argv[3], JSON.stringify(readmes));
} else { } else {
console.log(data); console.log(data);
} }

View file

@ -0,0 +1,113 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2023 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import "./contributorModal.css";
import { useSettings } from "@api/Settings";
import { classNameFactory } from "@api/Styles";
import ErrorBoundary from "@components/ErrorBoundary";
import { DevsById } from "@utils/constants";
import { fetchUserProfile, getTheme, Theme } from "@utils/discord";
import { ModalContent, ModalRoot, openModal } from "@utils/modal";
import { Forms, MaskedLink, showToast, useEffect, useMemo, UserProfileStore, useStateFromStores } from "@webpack/common";
import { User } from "discord-types/general";
import Plugins from "~plugins";
import { PluginCard } from ".";
const WebsiteIconDark = "/assets/e1e96d89e192de1997f73730db26e94f.svg";
const WebsiteIconLight = "/assets/730f58bcfd5a57a5e22460c445a0c6cf.svg";
const GithubIconLight = "/assets/3ff98ad75ac94fa883af5ed62d17c459.svg";
const GithubIconDark = "/assets/6a853b4c87fce386cbfef4a2efbacb09.svg";
const cl = classNameFactory("vc-author-modal-");
export function openContributorModal(user: User) {
openModal(modalProps =>
<ModalRoot {...modalProps}>
<ErrorBoundary>
<ModalContent className={cl("root")}>
<ContributorModal user={user} />
</ModalContent>
</ErrorBoundary>
</ModalRoot>
);
}
function GithubIcon() {
const src = getTheme() === Theme.Light ? GithubIconLight : GithubIconDark;
return <img src={src} alt="GitHub" />;
}
function WebsiteIcon() {
const src = getTheme() === Theme.Light ? WebsiteIconLight : WebsiteIconDark;
return <img src={src} alt="Website" />;
}
function ContributorModal({ user }: { user: User; }) {
useSettings();
const profile = useStateFromStores([UserProfileStore], () => UserProfileStore.getUserProfile(user.id));
useEffect(() => {
if (!profile && !user.bot && user.id)
fetchUserProfile(user.id);
}, [user.id]);
const githubName = profile?.connectedAccounts?.find(a => a.type === "github")?.name;
const website = profile?.connectedAccounts?.find(a => a.type === "domain")?.name;
const plugins = useMemo(() => {
const allPlugins = Object.values(Plugins);
const pluginsByAuthor = DevsById[user.id]
? allPlugins.filter(p => p.authors.includes(DevsById[user.id]))
: allPlugins.filter(p => p.authors.some(a => a.name === user.username));
return pluginsByAuthor
.filter(p => !p.name.endsWith("API"))
.sort((a, b) => Number(a.required ?? false) - Number(b.required ?? false));
}, [user.id, user.username]);
return (
<>
<div className={cl("header")}>
<img
className={cl("avatar")}
src={user.getAvatarURL(void 0, 512, true)}
alt=""
/>
<Forms.FormTitle tag="h2" className={cl("name")}>{user.username}</Forms.FormTitle>
<div className={cl("links")}>
{website && (
<MaskedLink
href={"https://" + website}
>
<WebsiteIcon />
</MaskedLink>
)}
{githubName && (
<MaskedLink href={`https://github.com/${githubName}`}>
<GithubIcon />
</MaskedLink>
)}
</div>
</div>
<div className={cl("plugins")}>
{plugins.map(p =>
<PluginCard
key={p.name}
plugin={p}
disabled={p.required ?? false}
onRestartNeeded={() => showToast("Restart to apply changes!")}
/>
)}
</div>
</>
);
}

View file

@ -18,7 +18,6 @@
import { generateId } from "@api/Commands"; import { generateId } from "@api/Commands";
import { useSettings } from "@api/Settings"; import { useSettings } from "@api/Settings";
import { disableStyle, enableStyle } from "@api/Styles";
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { Flex } from "@components/Flex"; import { Flex } from "@components/Flex";
import { proxyLazy } from "@utils/lazy"; import { proxyLazy } from "@utils/lazy";
@ -28,7 +27,7 @@ import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, M
import { LazyComponent } from "@utils/react"; import { LazyComponent } from "@utils/react";
import { OptionType, Plugin } from "@utils/types"; import { OptionType, Plugin } from "@utils/types";
import { findByCode, findByPropsLazy } from "@webpack"; import { findByCode, findByPropsLazy } from "@webpack";
import { Button, FluxDispatcher, Forms, React, Text, Tooltip, UserStore, UserUtils } from "@webpack/common"; import { Button, Clickable, FluxDispatcher, Forms, React, Text, Tooltip, UserStore, UserUtils } from "@webpack/common";
import { User } from "discord-types/general"; import { User } from "discord-types/general";
import { Constructor } from "type-fest"; import { Constructor } from "type-fest";
@ -41,7 +40,7 @@ import {
SettingSliderComponent, SettingSliderComponent,
SettingTextComponent SettingTextComponent
} from "./components"; } from "./components";
import hideBotTagStyle from "./userPopoutHideBotTag.css?managed"; import { openContributorModal } from "./ContributorModal";
const UserSummaryItem = LazyComponent(() => findByCode("defaultRenderUser", "showDefaultAvatarsForNullUsers")); const UserSummaryItem = LazyComponent(() => findByCode("defaultRenderUser", "showDefaultAvatarsForNullUsers"));
const AvatarStyles = findByPropsLazy("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar"); const AvatarStyles = findByPropsLazy("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar");
@ -92,27 +91,16 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
const hasSettings = Boolean(pluginSettings && plugin.options && !isObjectEmpty(plugin.options)); const hasSettings = Boolean(pluginSettings && plugin.options && !isObjectEmpty(plugin.options));
React.useEffect(() => { React.useEffect(() => {
enableStyle(hideBotTagStyle);
let originalUser: User;
(async () => { (async () => {
for (const user of plugin.authors.slice(0, 6)) { for (const user of plugin.authors.slice(0, 6)) {
const author = user.id const author = user.id
? await UserUtils.fetchUser(`${user.id}`) ? await UserUtils.fetchUser(`${user.id}`)
// only show name & pfp and no actions so users cannot harass plugin devs for support (send dms, add as friend, etc)
.then(u => (originalUser = u, makeDummyUser(u)))
.catch(() => makeDummyUser({ username: user.name })) .catch(() => makeDummyUser({ username: user.name }))
: makeDummyUser({ username: user.name }); : makeDummyUser({ username: user.name });
setAuthors(a => [...a, author]); setAuthors(a => [...a, author]);
} }
})(); })();
return () => {
disableStyle(hideBotTagStyle);
if (originalUser)
FluxDispatcher.dispatch({ type: "USER_UPDATE", user: originalUser });
};
}, []); }, []);
async function saveAndClose() { async function saveAndClose() {
@ -214,6 +202,19 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
showDefaultAvatarsForNullUsers showDefaultAvatarsForNullUsers
showUserPopout showUserPopout
renderMoreUsers={renderMoreUsers} renderMoreUsers={renderMoreUsers}
renderUser={(user: User) => (
<Clickable
className={AvatarStyles.clickableAvatar}
onClick={() => openContributorModal(user)}
>
<img
className={AvatarStyles.avatar}
src={user.getAvatarURL(void 0, 80, true)}
alt={user.username}
title={user.username}
/>
</Clickable>
)}
/> />
</div> </div>
</Forms.FormSection> </Forms.FormSection>

View file

@ -0,0 +1,57 @@
.vc-author-modal-root {
padding: 1em;
}
.vc-author-modal-header {
display: flex;
align-items: center;
margin-bottom: 1em;
}
.vc-author-modal-name {
text-transform: none;
flex-grow: 0;
background: var(--background-tertiary);
border-radius: 0 9999px 9999px 0;
padding: 6px 0.8em 6px 0.5em;
font-size: 20px;
height: 20px;
position: relative;
}
.vc-author-modal-name::before {
content: "";
display: block;
position: absolute;
height: 100%;
width: 16px;
background: var(--background-tertiary);
z-index: -1;
left: -16px;
top: 0;
}
.vc-author-modal-avatar {
height: 32px;
width: 32px;
border-radius: 50%;
}
.vc-author-modal-links {
margin-left: auto;
display: flex;
gap: 0.2em;
}
.vc-author-modal-links img {
height: 32px;
width: 32px;
border-radius: 50%;
border: 4px solid var(--background-tertiary);
box-sizing: border-box
}
.vc-author-modal-plugins {
display: grid;
gap: 0.5em;
}

View file

@ -91,7 +91,7 @@ interface PluginCardProps extends React.HTMLProps<HTMLDivElement> {
isNew?: boolean; isNew?: boolean;
} }
function PluginCard({ plugin, disabled, onRestartNeeded, onMouseEnter, onMouseLeave, isNew }: PluginCardProps) { export function PluginCard({ plugin, disabled, onRestartNeeded, onMouseEnter, onMouseLeave, isNew }: PluginCardProps) {
const settings = Settings.plugins[plugin.name]; const settings = Settings.plugins[plugin.name];
const isEnabled = () => settings.enabled ?? false; const isEnabled = () => settings.enabled ?? false;

View file

@ -1,3 +0,0 @@
[class|="userPopoutOuter"] [class*="botTag"] {
display: none;
}

View file

@ -8,6 +8,7 @@
width: 100%; width: 100%;
transition: 0.1s ease-out; transition: 0.1s ease-out;
transition-property: box-shadow, transform, background, opacity; transition-property: box-shadow, transform, background, opacity;
box-sizing: border-box;
} }
.vc-addon-card-disabled { .vc-addon-card-disabled {

View file

@ -49,7 +49,7 @@ export default definePlugin({
replace: "$1,vcProps=$2$3+(vcProps.channel.nsfw?' vc-nsfw-img':'')" replace: "$1,vcProps=$2$3+(vcProps.channel.nsfw?' vc-nsfw-img':'')"
}, { }, {
match: /(\.renderAttachments=.+?(.)=this\.props)(.+?\.embedWrapper)/g, match: /(\.renderAttachments=.+?(.)=this\.props)(.+?\.embedWrapper)/g,
replace: "$1,vcProps=$2$3+(vcProps.channel.nsfw?' vc-nsfw-img':'')" replace: "$1,vcProps=$2$3+(vcProps.nsfw?' vc-nsfw-img':'')"
}] }]
} }
], ],

View file

@ -72,6 +72,12 @@ const enum ActivityFlag {
INSTANCE = 1 << 0, INSTANCE = 1 << 0,
} }
const enum NameFormat {
StatusName = "status-name",
ArtistFirst = "artist-first",
SongFirst = "song-first",
}
const applicationId = "1108588077900898414"; const applicationId = "1108588077900898414";
const placeholderId = "2a96cbd8b46e442fc41c2b86b821562f"; const placeholderId = "2a96cbd8b46e442fc41c2b86b821562f";
@ -117,10 +123,29 @@ const settings = definePluginSettings({
default: true, default: true,
}, },
statusName: { statusName: {
description: "text shown in status", description: "custom status text",
type: OptionType.STRING, type: OptionType.STRING,
default: "some music", default: "some music",
}, },
nameFormat: {
description: "Show name of song and artist in status name",
type: OptionType.SELECT,
options: [
{
label: "Use custom status name",
value: NameFormat.StatusName,
default: true
},
{
label: "Use format 'artist - song'",
value: NameFormat.ArtistFirst
},
{
label: "Use format 'song - artist'",
value: NameFormat.SongFirst
}
],
},
useListeningStatus: { useListeningStatus: {
description: 'show "Listening to" status instead of "Playing"', description: 'show "Listening to" status instead of "Playing"',
type: OptionType.BOOLEAN, type: OptionType.BOOLEAN,
@ -140,13 +165,13 @@ const settings = definePluginSettings({
value: "placeholder" value: "placeholder"
} }
], ],
} },
}); });
export default definePlugin({ export default definePlugin({
name: "LastFMRichPresence", name: "LastFMRichPresence",
description: "Little plugin for Last.fm rich presence", description: "Little plugin for Last.fm rich presence",
authors: [Devs.dzshn, Devs.RuiNtD], authors: [Devs.dzshn, Devs.RuiNtD, Devs.blahajZip],
settingsAboutComponent: () => ( settingsAboutComponent: () => (
<> <>
@ -267,9 +292,20 @@ export default definePlugin({
url: `https://www.last.fm/user/${settings.store.username}`, url: `https://www.last.fm/user/${settings.store.username}`,
}); });
const statusName = (() => {
switch (settings.store.nameFormat) {
case NameFormat.ArtistFirst:
return trackData.artist + " - " + trackData.name;
case NameFormat.SongFirst:
return trackData.name + " - " + trackData.artist;
default:
return settings.store.statusName;
}
})();
return { return {
application_id: applicationId, application_id: applicationId,
name: settings.store.statusName, name: statusName,
details: trackData.name, details: trackData.name,
state: trackData.artist, state: trackData.artist,

View file

@ -0,0 +1,83 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2023 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { definePluginSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
import { React, Tooltip } from "@webpack/common";
const settings = definePluginSettings({
loop: {
description: "Whether to make the PiP video loop or not",
type: OptionType.BOOLEAN,
default: true,
restartNeeded: false
}
});
export default definePlugin({
name: "PictureInPicture",
description: "Adds picture in picture to videos (next to the Download button)",
authors: [Devs.Lumap],
settings,
patches: [
{
find: ".onRemoveAttachment,",
replacement: {
match: /\.nonMediaAttachment.{0,10}children:\[(\i),/,
replace: "$&$1&&$self.renderPiPButton(),"
},
},
],
renderPiPButton: ErrorBoundary.wrap(() => {
return (
<Tooltip text="Toggle Picture in Picture">
{tooltipProps => (
<div
{...tooltipProps}
role="button"
style={{
cursor: "pointer",
paddingTop: "4px",
paddingLeft: "4px",
paddingRight: "4px",
}}
onClick={e => {
const video = e.currentTarget.parentNode!.parentNode!.querySelector("video")!;
const videoClone = document.body.appendChild(video.cloneNode(true)) as HTMLVideoElement;
videoClone.loop = settings.store.loop;
videoClone.style.display = "none";
videoClone.onleavepictureinpicture = () => videoClone.remove();
function launchPiP() {
videoClone.currentTime = video.currentTime;
videoClone.requestPictureInPicture();
video.pause();
videoClone.play();
}
if (videoClone.readyState === 4 /* HAVE_ENOUGH_DATA */)
launchPiP();
else
videoClone.onloadedmetadata = launchPiP;
}}
>
<svg width="24px" height="24px" viewBox="0 0 24 24">
<path
fill="var(--interactive-normal)"
d="M21 3a1 1 0 0 1 1 1v7h-2V5H4v14h6v2H3a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h18zm0 10a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1h-8a1 1 0 0 1-1-1v-6a1 1 0 0 1 1-1h8zm-1 2h-6v4h6v-4z"
/>
</svg>
</div>
)}
</Tooltip>
);
}, { noop: true })
});

View file

@ -21,14 +21,11 @@ import { VENCORD_USER_AGENT } from "@utils/constants";
import { debounce } from "@utils/debounce"; import { debounce } from "@utils/debounce";
import { getCurrentChannel } from "@utils/discord"; import { getCurrentChannel } from "@utils/discord";
import { useAwaiter } from "@utils/react"; import { useAwaiter } from "@utils/react";
import { findStoreLazy } from "@webpack"; import { UserProfileStore, UserStore } from "@webpack/common";
import { UserStore } from "@webpack/common";
import { settings } from "./settings"; import { settings } from "./settings";
import { PronounCode, PronounMapping, PronounsResponse } from "./types"; import { PronounCode, PronounMapping, PronounsResponse } from "./types";
const UserProfileStore = findStoreLazy("UserProfileStore");
type PronounsWithSource = [string | null, string]; type PronounsWithSource = [string | null, string];
const EmptyPronouns: PronounsWithSource = [null, ""]; const EmptyPronouns: PronounsWithSource = [null, ""];

View file

@ -27,13 +27,12 @@ import { copyWithToast } from "@utils/misc";
import { LazyComponent } from "@utils/react"; import { LazyComponent } from "@utils/react";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findByCode, findByCodeLazy, findByPropsLazy, findStoreLazy } from "@webpack"; import { findByCode, findByCodeLazy, findByPropsLazy, findStoreLazy } from "@webpack";
import { Text, Tooltip } from "@webpack/common"; import { Text, Tooltip, UserProfileStore } from "@webpack/common";
import { User } from "discord-types/general"; import { User } from "discord-types/general";
import { VerifiedIcon } from "./VerifiedIcon"; import { VerifiedIcon } from "./VerifiedIcon";
const Section = LazyComponent(() => findByCode("().lastSection")); const Section = LazyComponent(() => findByCode("().lastSection"));
const UserProfileStore = findStoreLazy("UserProfileStore");
const ThemeStore = findStoreLazy("ThemeStore"); const ThemeStore = findStoreLazy("ThemeStore");
const platforms: { get(type: string): ConnectionPlatform; } = findByPropsLazy("isSupported", "getByUrl"); const platforms: { get(type: string): ConnectionPlatform; } = findByPropsLazy("isSupported", "getByUrl");
const getTheme: (user: User, displayProfile: any) => any = findByCodeLazy(',"--profile-gradient-primary-color"'); const getTheme: (user: User, displayProfile: any) => any = findByCodeLazy(',"--profile-gradient-primary-color"');

View file

@ -48,7 +48,7 @@ export default definePlugin({
name: "vencord-debug", name: "vencord-debug",
description: "Send Vencord Debug info", description: "Send Vencord Debug info",
predicate: ctx => AllowedChannelIds.includes(ctx.channel.id), predicate: ctx => AllowedChannelIds.includes(ctx.channel.id),
execute() { async execute() {
const { RELEASE_CHANNEL } = window.GLOBAL_ENV; const { RELEASE_CHANNEL } = window.GLOBAL_ENV;
const client = (() => { const client = (() => {
@ -75,6 +75,10 @@ export default definePlugin({
OpenAsar: "openasar" in window, OpenAsar: "openasar" in window,
}; };
if (IS_DISCORD_DESKTOP) {
info["Last Crash Reason"] = (await DiscordNative.processUtils.getLastCrash())?.rendererCrashReason ?? "N/A";
}
const debugInfo = ` const debugInfo = `
**Vencord Debug Info** **Vencord Debug Info**
>>> ${Object.entries(info).map(([k, v]) => `${k}: ${v}`).join("\n")} >>> ${Object.entries(info).map(([k, v]) => `${k}: ${v}`).join("\n")}

View file

@ -355,6 +355,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({
name: "bb010g", name: "bb010g",
id: 72791153467990016n, id: 72791153467990016n,
}, },
Lumap: {
name: "lumap",
id: 635383782576357407n
},
Dolfies: { Dolfies: {
name: "Dolfies", name: "Dolfies",
id: 852892297661906993n, id: 852892297661906993n,
@ -363,6 +367,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({
name: "RuukuLada", name: "RuukuLada",
id: 119705748346241027n, id: 119705748346241027n,
}, },
blahajZip: {
name: "blahaj.zip",
id: 683954422241427471n,
}
} satisfies Record<string, Dev>); } satisfies Record<string, Dev>);
// iife so #__PURE__ works correctly // iife so #__PURE__ works correctly

View file

@ -18,7 +18,7 @@
import { MessageObject } from "@api/MessageEvents"; import { MessageObject } from "@api/MessageEvents";
import { findByCodeLazy, findByPropsLazy, findLazy } from "@webpack"; import { findByCodeLazy, findByPropsLazy, findLazy } from "@webpack";
import { ChannelStore, ComponentDispatch, GuildStore, MaskedLink, ModalImageClasses, PrivateChannelsStore, SelectedChannelStore, SelectedGuildStore, UserUtils } from "@webpack/common"; import { ChannelStore, ComponentDispatch, FluxDispatcher, GuildStore, MaskedLink, ModalImageClasses, PrivateChannelsStore, RestAPI, SelectedChannelStore, SelectedGuildStore, UserProfileStore, UserUtils } from "@webpack/common";
import { Guild, Message, User } from "discord-types/general"; import { Guild, Message, User } from "discord-types/general";
import { ImageModal, ModalRoot, ModalSize, openModal } from "./modal"; import { ImageModal, ModalRoot, ModalSize, openModal } from "./modal";
@ -118,6 +118,41 @@ export async function openUserProfile(id: string) {
}); });
} }
interface FetchUserProfileOptions {
friend_token?: string;
connections_role_id?: string;
guild_id?: string;
with_mutual_guilds?: boolean;
with_mutual_friends_count?: boolean;
}
/**
* Fetch a user's profile
*/
export async function fetchUserProfile(id: string, options?: FetchUserProfileOptions) {
const cached = UserProfileStore.getUserProfile(id);
if (cached) return cached;
FluxDispatcher.dispatch({ type: "USER_PROFILE_FETCH_START", userId: id });
const { body } = await RestAPI.get({
url: `/users/${id}/profile`,
query: {
with_mutual_guilds: false,
with_mutual_friends_count: false,
...options
},
oldFormErrors: true,
});
FluxDispatcher.dispatch({ type: "USER_UPDATE", user: body.user });
await FluxDispatcher.dispatch({ type: "USER_PROFILE_FETCH_SUCCESS", ...body });
if (options?.guild_id && body.guild_member)
FluxDispatcher.dispatch({ type: "GUILD_MEMBER_PROFILE_UPDATE", guildId: options.guild_id, guildMember: body.guild_member });
return UserProfileStore.getUserProfile(id);
}
/** /**
* Get the unique username for a user. Returns user.username for pomelo people, user.tag otherwise * Get the unique username for a user. Returns user.username for pomelo people, user.tag otherwise
*/ */

View file

@ -48,6 +48,7 @@ export let PoggerModeSettingsStore: GenericStore;
export let GuildStore: Stores.GuildStore & t.FluxStore; export let GuildStore: Stores.GuildStore & t.FluxStore;
export let UserStore: Stores.UserStore & t.FluxStore; export let UserStore: Stores.UserStore & t.FluxStore;
export let UserProfileStore: GenericStore;
export let SelectedChannelStore: Stores.SelectedChannelStore & t.FluxStore; export let SelectedChannelStore: Stores.SelectedChannelStore & t.FluxStore;
export let SelectedGuildStore: t.FluxStore & Record<string, any>; export let SelectedGuildStore: t.FluxStore & Record<string, any>;
export let ChannelStore: Stores.ChannelStore & t.FluxStore; export let ChannelStore: Stores.ChannelStore & t.FluxStore;
@ -86,6 +87,7 @@ export const useStateFromStores: <T>(
waitForStore("DraftStore", s => DraftStore = s); waitForStore("DraftStore", s => DraftStore = s);
waitForStore("UserStore", s => UserStore = s); waitForStore("UserStore", s => UserStore = s);
waitForStore("UserProfileStore", m => UserProfileStore = m);
waitForStore("ChannelStore", m => ChannelStore = m); waitForStore("ChannelStore", m => ChannelStore = m);
waitForStore("SelectedChannelStore", m => SelectedChannelStore = m); waitForStore("SelectedChannelStore", m => SelectedChannelStore = m);
waitForStore("SelectedGuildStore", m => SelectedGuildStore = m); waitForStore("SelectedGuildStore", m => SelectedGuildStore = m);

View file

@ -398,12 +398,18 @@ export type Paginator = ComponentType<{
hideMaxPage?: boolean; hideMaxPage?: boolean;
}>; }>;
export type MaskedLink = ComponentType<{ export type MaskedLink = ComponentType<PropsWithChildren<{
onClick(): void;
trusted: boolean;
title: string,
href: string; href: string;
}>; rel?: string;
target?: string;
title?: string,
className?: string;
tabIndex?: number;
onClick?(): void;
trusted?: boolean;
messageId?: string;
channelId?: string;
}>>;
export type ScrollerThin = ComponentType<PropsWithChildren<{ export type ScrollerThin = ComponentType<PropsWithChildren<{
className?: string; className?: string;