From e87ced9f9dfbe9012ce25a85ec6c91cc94061f8a Mon Sep 17 00:00:00 2001 From: D3SOX Date: Mon, 15 Apr 2024 00:19:52 +0200 Subject: [PATCH] feat(memberListActivities): better tooltips --- src/plugins/memberListActivities/index.tsx | 102 ++++++++++++++------ src/plugins/memberListActivities/styles.css | 34 +++++++ 2 files changed, 109 insertions(+), 27 deletions(-) diff --git a/src/plugins/memberListActivities/index.tsx b/src/plugins/memberListActivities/index.tsx index 182108f85..1ca32f26f 100644 --- a/src/plugins/memberListActivities/index.tsx +++ b/src/plugins/memberListActivities/index.tsx @@ -24,7 +24,8 @@ import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; 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 { SpotifyIcon } from "./components/SpotifyIcon"; @@ -104,6 +105,7 @@ interface ApplicationIcon { alt: string; }; activity: Activity; + application?: Application; } interface ActivityListIcon { @@ -124,7 +126,47 @@ const DefaultActivityIcon = findComponentByCodeLazy("M6,7 L2,7 L2,6 L6,6 L6,7 Z const fetchedApplications = new Map(); -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 ( + +
+ {image && Activity logo} +
{activity.name}
+ {hasDetails &&
} +
+
{activity.details}
+
{activity.state}
+
+
+ + ); +}; + function getApplicationIcons(activities: Activity[]) { const applicationIcons: ApplicationIcon[] = []; @@ -183,12 +225,14 @@ function getApplicationIcons(activities: Activity[]) { const src = `https://cdn.discordapp.com/app-icons/${application.id}/${application.icon}.png`; applicationIcons.push({ image: { src, alt: application.name }, - activity + activity, + application }); } else if (platform === "xbox") { applicationIcons.push({ image: { src: xboxUrl, alt: "Xbox" }, - activity + activity, + application }); } } @@ -213,17 +257,23 @@ export default definePlugin({ settings, - patchActivityList: (activities: Activity[]): JSX.Element | null => { + patchActivityList: ({ activities, user }: { activities: Activity[], user: User }): JSX.Element | null => { const icons: ActivityListIcon[] = []; const spotifyActivity = activities.find(({ name }) => name === "Spotify"); if (spotifyActivity) { - icons.push({ iconElement: , tooltip: spotifyActivity.details ?? spotifyActivity.name }); + icons.push({ + iconElement: , + tooltip: + }); } const twitchActivity = activities.find(({ name }) => name === "Twitch"); if (twitchActivity) { - icons.push({ iconElement: , tooltip: twitchActivity.details ?? twitchActivity.name }); + icons.push({ + iconElement: , + tooltip: + }); } const applicationIcons = getApplicationIcons(activities); if (applicationIcons.length) { @@ -236,7 +286,7 @@ export default definePlugin({ for (const appIcon of uniqueIcons) { icons.push({ iconElement: , - tooltip: appIcon.activity.details ?? appIcon.activity.name + tooltip: }); } } @@ -244,24 +294,22 @@ export default definePlugin({ if (icons.length) { return
- {icons.map(({ iconElement, tooltip }, i) => { - return ( -
- {tooltip ? - {({ onMouseEnter, onMouseLeave }) => ( -
- {iconElement} -
- )} -
: iconElement} -
- ); - })} + {icons.map(({ iconElement, tooltip }, i) => ( +
+ {tooltip ? + {({ onMouseEnter, onMouseLeave }) => ( +
+ {iconElement} +
+ )} +
: iconElement} +
+ ))}
; } else { @@ -282,7 +330,7 @@ export default definePlugin({ find: "default.getHangStatusActivity():null!", replacement: { match: /null!=(\i)&&\i.some\(\i=>\(0,\i.default\)\(\i,\i\)\)\?/, - replace: "$self.patchActivityList($1),false?" + replace: "$self.patchActivityList(e),false?" } }, ], diff --git a/src/plugins/memberListActivities/styles.css b/src/plugins/memberListActivities/styles.css index ce87e633c..22b2af21a 100644 --- a/src/plugins/memberListActivities/styles.css +++ b/src/plugins/memberListActivities/styles.css @@ -18,3 +18,37 @@ object-fit: cover; 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); +}