feat(memberListActivities): add tooltips

This commit is contained in:
D3SOX 2024-04-02 19:46:33 +02:00
parent 3cfefad3f5
commit 99420e2cbe
No known key found for this signature in database
GPG key ID: 39EC1673FC37B048

View file

@ -24,6 +24,8 @@ import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy, findComponentByCodeLazy, findStoreLazy } from "@webpack"; import { findByPropsLazy, findComponentByCodeLazy, findStoreLazy } from "@webpack";
import { Tooltip } from "@webpack/common";
import type { ImgHTMLAttributes } from "react";
import { SpotifyIcon } from "./components/SpotifyIcon"; import { SpotifyIcon } from "./components/SpotifyIcon";
import { TwitchIcon } from "./components/TwitchIcon"; import { TwitchIcon } from "./components/TwitchIcon";
@ -97,6 +99,17 @@ interface Executable {
is_launcher: boolean; is_launcher: boolean;
} }
type ImageAttributes = ImgHTMLAttributes<HTMLImageElement> & {
src: string;
alt: string;
activity: Activity;
};
interface ActivityListIcon {
iconElement: JSX.Element;
tooltip?: JSX.Element | string;
}
const ApplicationStore: { const ApplicationStore: {
getApplication: (id: string) => Application | null; getApplication: (id: string) => Application | null;
} = findStoreLazy("ApplicationStore"); } = findStoreLazy("ApplicationStore");
@ -112,30 +125,14 @@ const fetchedApplications = new Map<string, Application | null>();
const xboxUrl = "https://discord.com/assets/9a15d086141be29d9fcd.png"; const xboxUrl = "https://discord.com/assets/9a15d086141be29d9fcd.png";
export default definePlugin({ function getApplicationIcons(activities: Activity[]) {
name: "MemberListActivities", const applicationIcons: ImageAttributes[] = [];
description: "Shows activity icons in the member list",
authors: [Devs.D3SOX],
tags: ["activity"],
settings,
patchActivityList: (activities: Activity[]): JSX.Element | null => {
const icons: JSX.Element[] = [];
if (activities.some(activity => activity.name === "Spotify")) {
icons.push(<SpotifyIcon />);
}
if (activities.some(activity => activity.name === "Twitch")) {
icons.push(<TwitchIcon />);
}
const applications = activities.filter(activity => activity.application_id || activity.platform); const applications = activities.filter(activity => activity.application_id || activity.platform);
applications.forEach(activity => {
for (const activity of applications) {
const { assets, application_id, platform } = activity; const { assets, application_id, platform } = activity;
if (!application_id && !platform) { if (!application_id && !platform) {
return; continue;
} }
if (assets) { if (assets) {
@ -143,11 +140,11 @@ export default definePlugin({
if (image.startsWith("mp:")) { if (image.startsWith("mp:")) {
const discordMediaLink = `https://media.discordapp.net/${image.replace(/mp:/, "")}`; const discordMediaLink = `https://media.discordapp.net/${image.replace(/mp:/, "")}`;
if (settings.store.renderGifs || !discordMediaLink.endsWith(".gif")) { if (settings.store.renderGifs || !discordMediaLink.endsWith(".gif")) {
icons.push(<img src={discordMediaLink} alt={alt}/>); applicationIcons.push({ src: discordMediaLink, alt, activity });
} }
} else { } else {
const src = `https://cdn.discordapp.com/app-assets/${application_id}/${image}.png`; const src = `https://cdn.discordapp.com/app-assets/${application_id}/${image}.png`;
icons.push(<img src={src} alt={alt}/>); applicationIcons.push({ src, alt, activity });
} }
}; };
@ -177,32 +174,78 @@ export default definePlugin({
if (application) { if (application) {
if (application.icon) { if (application.icon) {
const src = `https://cdn.discordapp.com/app-icons/${application.id}/${application.icon}.png`; const src = `https://cdn.discordapp.com/app-icons/${application.id}/${application.icon}.png`;
icons.push(<img src={src} alt={application.name} />); applicationIcons.push({ src, alt: application.name, activity });
} else if (platform === "xbox") { } else if (platform === "xbox") {
icons.push(<img src={xboxUrl} alt="Xbox" />); applicationIcons.push({ src: xboxUrl, alt: "Xbox", activity });
} }
} }
} else { } else {
if (platform === "xbox") { if (platform === "xbox") {
icons.push(<img src={xboxUrl} alt="Xbox" />); applicationIcons.push({ src: xboxUrl, alt: "Xbox", activity });
} }
} }
}
return applicationIcons;
}
export default definePlugin({
name: "MemberListActivities",
description: "Shows activity icons in the member list",
authors: [Devs.D3SOX],
tags: ["activity"],
settings,
patchActivityList: (activities: Activity[]): JSX.Element | null => {
const icons: ActivityListIcon[] = [];
const spotifyActivity = activities.find(({ name }) => name === "Spotify");
if (spotifyActivity) {
icons.push({ iconElement: <SpotifyIcon />, tooltip: spotifyActivity.details ?? spotifyActivity.name });
}
const twitchActivity = activities.find(({ name }) => name === "Twitch");
if (twitchActivity) {
icons.push({ iconElement: <TwitchIcon />, tooltip: twitchActivity.details ?? twitchActivity.name });
}
const applicationIcons = getApplicationIcons(activities);
if (applicationIcons.length) {
const compareImageSource = (a: ImageAttributes, b: ImageAttributes) => {
return a.src === b.src;
};
const uniqueIcons = applicationIcons.filter((element, index, array) => {
return array.findIndex(el => compareImageSource(el, element)) === index;
}); });
for (const iconAttrs of uniqueIcons) {
icons.push({
iconElement: <img {...iconAttrs} />,
tooltip: iconAttrs.activity.details ?? iconAttrs.activity.name
});
}
}
if (icons.length) { if (icons.length) {
const compareJSXElementsSource = (a: JSX.Element, b: JSX.Element) => {
return a.props?.src === b.props?.src;
};
const uniqueIcons = icons.filter((element, index, array) => {
return array.findIndex(el => compareJSXElementsSource(el, element)) === index;
});
return <ErrorBoundary noop> return <ErrorBoundary noop>
<div className={cl("row")}> <div className={cl("row")}>
{uniqueIcons.map((icon, i) => ( {icons.map(({ iconElement, tooltip }, i) => {
<div key={i} className={cl("icon")} style={{ width: `${settings.store.iconSize}px`, height: `${settings.store.iconSize}px` }}> return (
{icon} <div key={i} className={cl("icon")} style={{
width: `${settings.store.iconSize}px`,
height: `${settings.store.iconSize}px`
}}>
{tooltip ? <Tooltip text={tooltip}>
{({ onMouseEnter, onMouseLeave }) => (
<div
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}>
{iconElement}
</div> </div>
))} )}
</Tooltip> : iconElement}
</div>
);
})}
</div> </div>
</ErrorBoundary>; </ErrorBoundary>;
} else { } else {