feat(memberListActivities): show elapsed time in tooltip and support twitch images

This commit is contained in:
D3SOX 2024-04-18 23:32:21 +02:00
parent 7fee537ea1
commit 957639be6c
No known key found for this signature in database
GPG key ID: 39EC1673FC37B048

View file

@ -24,7 +24,7 @@ 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 { React, Tooltip, useMemo } from "@webpack/common"; import { moment, React, Tooltip, useMemo } from "@webpack/common";
import { User } from "discord-types/general"; import { User } from "discord-types/general";
import { SpotifyIcon } from "./components/SpotifyIcon"; import { SpotifyIcon } from "./components/SpotifyIcon";
@ -71,16 +71,24 @@ const fetchedApplications = new Map<string, Application | null>();
const xboxUrl = "https://discord.com/assets/9a15d086141be29d9fcd.png"; // TODO: replace with "renderXboxImage"? const xboxUrl = "https://discord.com/assets/9a15d086141be29d9fcd.png"; // TODO: replace with "renderXboxImage"?
function getActivityImage(activity: Activity): string | undefined { function getActivityImage(activity: Activity, application?: Application): string | undefined {
if (activity.type === 2 && activity.name === "Spotify") { if (activity.type === 2 && activity.name === "Spotify") {
// get either from large or small image // get either from large or small image
const image = activity.assets?.large_image ?? activity.assets?.small_image; const image = activity.assets?.large_image ?? activity.assets?.small_image;
// image needs to replace 'spotify: // image needs to replace 'spotify:'
if (image?.startsWith("spotify:")) { if (image?.startsWith("spotify:")) {
// spotify cover art is always https://i.scdn.co/image/ID // spotify cover art is always https://i.scdn.co/image/ID
return image.replace("spotify:", "https://i.scdn.co/image/"); return image.replace("spotify:", "https://i.scdn.co/image/");
} }
} }
if (activity.type === 1 && activity.name === "Twitch") {
const image = activity.assets?.large_image;
// image needs to replace 'twitch:'
if (image?.startsWith("twitch:")) {
// twitch images are always https://static-cdn.jtvnw.net/previews-ttv/live_user_USERNAME-RESOLTUON.jpg
return `${image.replace("twitch:", "https://static-cdn.jtvnw.net/previews-ttv/live_user_")}-108x60.jpg`;
}
}
// TODO: we could support other assets here // TODO: we could support other assets here
} }
@ -91,9 +99,27 @@ function getValidTimestamps(activity: Activity): Required<Timestamp> | null {
return null; return null;
} }
const ActivityTooltip = ({ activity }: Readonly<{ activity: Activity }>) => { function getValidStartTimeStamp(activity: Activity): number | null {
if (activity.timestamps?.start !== undefined) {
return activity.timestamps.start;
}
return null;
}
const customFormat = (momentObj: moment.Moment): string => {
const hours = momentObj.hours();
const formattedTime = momentObj.format("mm:ss");
return hours > 0 ? `${momentObj.format("HH:")}${formattedTime}` : formattedTime;
};
function formatElapsedTime(startTime: moment.Moment, endTime: moment.Moment): string {
const duration = moment.duration(endTime.diff(startTime));
return `${customFormat(moment.utc(duration.asMilliseconds()))} elapsed`;
}
const ActivityTooltip = ({ activity, application }: Readonly<{ activity: Activity, application?: Application }>) => {
const image = useMemo(() => { const image = useMemo(() => {
const activityImage = getActivityImage(activity); const activityImage = getActivityImage(activity, application);
if (activityImage) { if (activityImage) {
return activityImage; return activityImage;
} }
@ -101,6 +127,7 @@ const ActivityTooltip = ({ activity }: Readonly<{ activity: Activity }>) => {
return icon?.image.src; return icon?.image.src;
}, [activity]); }, [activity]);
const timestamps = useMemo(() => getValidTimestamps(activity), [activity]); const timestamps = useMemo(() => getValidTimestamps(activity), [activity]);
const startTime = useMemo(() => getValidStartTimeStamp(activity), [activity]);
const hasDetails = activity.details ?? activity.state; const hasDetails = activity.details ?? activity.state;
return ( return (
@ -112,14 +139,18 @@ const ActivityTooltip = ({ activity }: Readonly<{ activity: Activity }>) => {
<div className={cl("activity-details")}> <div className={cl("activity-details")}>
<div>{activity.details}</div> <div>{activity.details}</div>
<div>{activity.state}</div> <div>{activity.state}</div>
{!timestamps && startTime &&
<div className={cl("activity-time-bar")}>
{formatElapsedTime(moment(startTime), moment())}
</div> </div>
{timestamps && <TimeBar start={timestamps.start} end={timestamps.end} themed={false} className={cl("activity-time-bar")}/>} }
</div>
{timestamps && <TimeBar start={timestamps.start} end={timestamps.end} themed={false} className={cl("activity-time-bar")}/> }
</div> </div>
</ErrorBoundary> </ErrorBoundary>
); );
}; };
function getApplicationIcons(activities: Activity[], preferSmall = false) { function getApplicationIcons(activities: Activity[], preferSmall = false) {
const applicationIcons: ApplicationIcon[] = []; const applicationIcons: ApplicationIcon[] = [];
const applications = activities.filter(activity => activity.application_id || activity.platform); const applications = activities.filter(activity => activity.application_id || activity.platform);
@ -245,7 +276,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: <ActivityTooltip activity={appIcon.activity} /> tooltip: <ActivityTooltip activity={appIcon.activity} application={appIcon.application} />
}); });
} }
} }