1
0
Fork 1
mirror of https://github.com/Vendicated/Vencord.git synced 2025-01-10 18:06:22 +00:00

feat(memberListActivities): better tooltips

This commit is contained in:
D3SOX 2024-04-15 00:19:52 +02:00
parent 0753b51104
commit e87ced9f9d
No known key found for this signature in database
GPG key ID: 39EC1673FC37B048
2 changed files with 109 additions and 27 deletions

View file

@ -24,7 +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 { Tooltip, useMemo } from "@webpack/common";
import { User } from "discord-types/general";
import type { ImgHTMLAttributes } from "react"; import type { ImgHTMLAttributes } from "react";
import { SpotifyIcon } from "./components/SpotifyIcon"; import { SpotifyIcon } from "./components/SpotifyIcon";
@ -104,6 +105,7 @@ interface ApplicationIcon {
alt: string; alt: string;
}; };
activity: Activity; activity: Activity;
application?: Application;
} }
interface ActivityListIcon { interface ActivityListIcon {
@ -124,7 +126,47 @@ const DefaultActivityIcon = findComponentByCodeLazy("M6,7 L2,7 L2,6 L6,6 L6,7 Z
const fetchedApplications = new Map<string, Application | null>(); const fetchedApplications = new Map<string, Application | null>();
const xboxUrl = "https://discord.com/assets/9a15d086141be29d9fcd.png"; const xboxUrl = "https://discord.com/assets/9a15d086141be29d9fcd.png"; // TODO: replace with "renderXboxImage"?
function getActivityImage(activity: Activity): string | undefined {
if (activity.type === 2 && activity.name === "Spotify") {
// get either from large or small image
const image = activity.assets?.large_image ?? activity.assets?.small_image;
// image needs to replace 'spotify:
if (image?.startsWith("spotify:")) {
// spotify cover art is always https://i.scdn.co/image/ID
return image.replace("spotify:", "https://i.scdn.co/image/");
}
}
// TODO: we could support other assets here, like showing the small/large image counterpart in comparison to that was shown in the activity list
}
const ActivityTooltip = ({ activity }: Readonly<{ activity: Activity }>) => {
const image = useMemo(() => {
const activityImage = getActivityImage(activity);
if (activityImage) {
return activityImage;
}
const icon = getApplicationIcons([activity])[0];
return icon?.image.src;
}, [activity]);
const hasDetails = activity.details ?? activity.state;
return (
<ErrorBoundary>
<div className={cl("activity")}>
{image && <img className={cl("activity-image")} src={image} alt="Activity logo" />}
<div className={cl("activity-title")}>{activity.name}</div>
{hasDetails && <div className={cl("activity-divider")} />}
<div className={cl("activity-details")}>
<div>{activity.details}</div>
<div>{activity.state}</div>
</div>
</div>
</ErrorBoundary>
);
};
function getApplicationIcons(activities: Activity[]) { function getApplicationIcons(activities: Activity[]) {
const applicationIcons: ApplicationIcon[] = []; const applicationIcons: ApplicationIcon[] = [];
@ -183,12 +225,14 @@ function getApplicationIcons(activities: Activity[]) {
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`;
applicationIcons.push({ applicationIcons.push({
image: { src, alt: application.name }, image: { src, alt: application.name },
activity activity,
application
}); });
} else if (platform === "xbox") { } else if (platform === "xbox") {
applicationIcons.push({ applicationIcons.push({
image: { src: xboxUrl, alt: "Xbox" }, image: { src: xboxUrl, alt: "Xbox" },
activity activity,
application
}); });
} }
} }
@ -213,17 +257,23 @@ export default definePlugin({
settings, settings,
patchActivityList: (activities: Activity[]): JSX.Element | null => { patchActivityList: ({ activities, user }: { activities: Activity[], user: User }): JSX.Element | null => {
const icons: ActivityListIcon[] = []; const icons: ActivityListIcon[] = [];
const spotifyActivity = activities.find(({ name }) => name === "Spotify"); const spotifyActivity = activities.find(({ name }) => name === "Spotify");
if (spotifyActivity) { if (spotifyActivity) {
icons.push({ iconElement: <SpotifyIcon />, tooltip: spotifyActivity.details ?? spotifyActivity.name }); icons.push({
iconElement: <SpotifyIcon />,
tooltip: <ActivityTooltip activity={spotifyActivity} />
});
} }
const twitchActivity = activities.find(({ name }) => name === "Twitch"); const twitchActivity = activities.find(({ name }) => name === "Twitch");
if (twitchActivity) { if (twitchActivity) {
icons.push({ iconElement: <TwitchIcon />, tooltip: twitchActivity.details ?? twitchActivity.name }); icons.push({
iconElement: <TwitchIcon />,
tooltip: <ActivityTooltip activity={twitchActivity} />
});
} }
const applicationIcons = getApplicationIcons(activities); const applicationIcons = getApplicationIcons(activities);
if (applicationIcons.length) { if (applicationIcons.length) {
@ -236,7 +286,7 @@ export default definePlugin({
for (const appIcon of uniqueIcons) { for (const appIcon of uniqueIcons) {
icons.push({ icons.push({
iconElement: <img {...appIcon.image} />, iconElement: <img {...appIcon.image} />,
tooltip: appIcon.activity.details ?? appIcon.activity.name tooltip: <ActivityTooltip activity={appIcon.activity} />
}); });
} }
} }
@ -244,24 +294,22 @@ export default definePlugin({
if (icons.length) { if (icons.length) {
return <ErrorBoundary noop> return <ErrorBoundary noop>
<div className={cl("row")}> <div className={cl("row")}>
{icons.map(({ iconElement, tooltip }, i) => { {icons.map(({ iconElement, tooltip }, i) => (
return ( <div key={i} className={cl("icon")} style={{
<div key={i} className={cl("icon")} style={{ width: `${settings.store.iconSize}px`,
width: `${settings.store.iconSize}px`, height: `${settings.store.iconSize}px`
height: `${settings.store.iconSize}px` }}>
}}> {tooltip ? <Tooltip text={tooltip}>
{tooltip ? <Tooltip text={tooltip}> {({ onMouseEnter, onMouseLeave }) => (
{({ onMouseEnter, onMouseLeave }) => ( <div
<div onMouseEnter={onMouseEnter}
onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>
onMouseLeave={onMouseLeave}> {iconElement}
{iconElement} </div>
</div> )}
)} </Tooltip> : iconElement}
</Tooltip> : iconElement} </div>
</div> ))}
);
})}
</div> </div>
</ErrorBoundary>; </ErrorBoundary>;
} else { } else {
@ -282,7 +330,7 @@ export default definePlugin({
find: "default.getHangStatusActivity():null!", find: "default.getHangStatusActivity():null!",
replacement: { replacement: {
match: /null!=(\i)&&\i.some\(\i=>\(0,\i.default\)\(\i,\i\)\)\?/, match: /null!=(\i)&&\i.some\(\i=>\(0,\i.default\)\(\i,\i\)\)\?/,
replace: "$self.patchActivityList($1),false?" replace: "$self.patchActivityList(e),false?"
} }
}, },
], ],

View file

@ -18,3 +18,37 @@
object-fit: cover; object-fit: cover;
border-radius: 50%; border-radius: 50%;
} }
.vc-mla-activity {
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
gap: 5px;
}
.vc-mla-activity-title {
font-weight: bold;
text-align: center;
}
.vc-mla-activity-image {
height: 20px;
width: 20px;
border-radius: 50%;
object-fit: cover;
}
.vc-mla-activity-divider {
width: 100%;
border-top: 1px dotted rgba(255, 255, 255, 0.2);
margin-top: 3px;
margin-bottom: 3px;
}
.vc-mla-activity-details {
display: flex;
flex-direction: column;
/* discord gray */
color: var(--text-muted);
}