Skip to content

Commit

Permalink
add All Activities
Browse files Browse the repository at this point in the history
  • Loading branch information
Cynosphere committed Nov 5, 2024
1 parent b065ae7 commit f3400f2
Show file tree
Hide file tree
Showing 5 changed files with 243 additions and 0 deletions.
57 changes: 57 additions & 0 deletions src/allActivities/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { ExtensionWebpackModule, Patch } from "@moonlight-mod/types";

export const patches: Patch[] = [
// Replace the existing activity/stream wrapper component with one we can add to easier
{
find: /user:.,currentUser:.,stream:.,className:.+?user:.,currentUser:.,activity:.,className:/,
replace: {
match: /:function\(\){return (.)}/,
replacement: ':function(){return require("allActivities_activities").default}'
}
},

// Make streams account for "Competing In"
{
find: '.STREAM_PREVIEW="StreamPreview"',
replace: {
match: /\(null==.\?void 0:.\.type\)!==.\.[a-zA-Z_$][\w$]*\.PLAYING&&/,
replacement: (orig: string) => orig + orig.replace("PLAYING", "COMPETING")
}
},

// Do not de-duplicate entries in useUserProfileActivity
{
find: '("use-user-profile-activity")',
replace: {
match: /\(0,.\.uniqWith\)/,
replacement: "((inp)=>inp)"
}
},

// Icons
{
find: ".lostPermission",
replace: {
match: /name:null==.\?(\(0,.\.jsx\))\("span"/,
replacement: (orig, createElement) =>
`children:${createElement}(require("allActivities_icons").default,{user:arguments[0].user}),${orig}`
},
prerequisite: () => moonlight.getConfigOption<boolean>("allActivities", "icons") ?? true
}
];

export const webpackModules: Record<string, ExtensionWebpackModule> = {
activities: {
dependencies: [{ id: "react" }, { ext: "spacepack", id: "spacepack" }]
},
icons: {
dependencies: [
{ ext: "spacepack", id: "spacepack" },
{ id: "react" },
{ ext: "common", id: "stores" },
{ id: "discord/Constants" },
{ id: "discord/components/common/index" },
'applicationStreamingPreviewWrapper:"applicationStreamingPreviewWrapper_'
]
}
};
28 changes: 28 additions & 0 deletions src/allActivities/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
"id": "allActivities",
"version": "1.0.0",
"meta": {
"name": "All Activities",
"tagline": "Shows all activities in user popouts, and optionally icons in the member list",
"authors": [
"Cynosphere"
],
"tags": [
"profiles"
],
"source": "https://github.com/Cynosphere/moonlight-extensions"
},
"settings": {
"icons": {
"displayName": "Enable activity icons in member list",
"type": "boolean",
"default": true
}
},
"dependencies": [
"spacepack",
"common"
],
"apiLevel": 2
}
13 changes: 13 additions & 0 deletions src/allActivities/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.allActivities-icons {
display: grid;
grid-auto-flow: column;
grid-gap: 4px;
}

.allActivities-iconCard {
width: 250px;
}

.allActivities-iconTooltip {
max-width: unset !important;
}
51 changes: 51 additions & 0 deletions src/allActivities/webpackModules/activities.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React from "@moonlight-mod/wp/react";
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";

const UserProfileActivityCardWrapper = spacepack.findByCode('location:"' + 'UserProfileActivityCardWrapper"')[0].exports
.Z;
const UserProfileStreamActivityCard = spacepack.findByCode('surface:"' + 'user-profile-stream-activity-card",')[0]
.exports.Z;
const useUserProfileActivity = spacepack.findByCode('("use-user-' + 'profile-activity")')[0].exports.Z;

type UserPopoutActivitiesProps = {
user: any; // no discord common types :(
currentUser: any;
className: string;
onClose: () => void;
};

export default function UserPopoutActivities({ user, currentUser, className, onClose }: UserPopoutActivitiesProps) {
const { live, stream } = useUserProfileActivity(user.id);
const [firstActivity] = live;
const activities = [...live];
activities.shift();

return [
null != stream ? (
<UserProfileStreamActivityCard
user={user}
currentUser={currentUser}
stream={stream}
className={className}
onClose={onClose}
/>
) : null != firstActivity ? (
<UserProfileActivityCardWrapper
user={user}
currentUser={currentUser}
activity={firstActivity}
className={className}
onClose={onClose}
/>
) : null,
...activities.map((activity: any) => (
<UserProfileActivityCardWrapper
user={user}
currentUser={currentUser}
activity={activity}
className={className}
onClose={onClose}
/>
))
];
}
94 changes: 94 additions & 0 deletions src/allActivities/webpackModules/icons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import React from "@moonlight-mod/wp/react";
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
import { Tooltip } from "@moonlight-mod/wp/discord/components/common/index";
import { useStateFromStores } from "@moonlight-mod/wp/discord/packages/flux";
import { ApplicationStore, GameStore, UserStore } from "@moonlight-mod/wp/common_stores";

// FIXME: mappings
const { ActivityTypes, PlatformTypes } = spacepack.require("discord/Constants");

const useUserProfileActivity = spacepack.findByCode('("use-user-' + 'profile-activity")')[0].exports.Z;
const ConnectionPlatforms = spacepack.findByExports("getByUrl", "get", "isSupported")[0].exports.Z;
const UserProfileActivityCard = spacepack.findByCode('location:"' + 'UserProfileActivityCard",')[0].exports.Z;

// findByExports is unreliable for whatever reason and causes every other reload of the client to fail????????????
//const ActivityClasses = spacepack.findByExports("multipleIconWrapper", "headerIcon")[0].exports;
const ActivityClasses = spacepack.findByCode(
"applicationStreamingPreviewWrapper:" + '"applicationStreamingPreviewWrapper_'
)[0].exports;

const SpotifyIcon = ConnectionPlatforms.get(PlatformTypes.SPOTIFY).icon.lightSVG;
const TwitchIcon = ConnectionPlatforms.get(PlatformTypes.TWITCH).icon.lightSVG;

type ActivityIconsProps = {
user: any;
};
type ActivityIconProps = {
user: any;
currentUser: any;
activity: any;
};
type ActivityIconIconProps = {
card: React.ReactNode;
icon: string;
};

function ActivityIconIcon({ card, icon }: ActivityIconIconProps) {
return (
<Tooltip text={card} position="left" tooltipClassName="allActivities-iconTooltip">
{(tooltipProps: any) => <img {...tooltipProps} className={ActivityClasses.headerIcon} src={icon} />}
</Tooltip>
);
}

function ActivityIcon({ user, currentUser, activity }: ActivityIconProps) {
const game = useStateFromStores([GameStore, ApplicationStore], () => {
return activity != null && activity.application_id
? ApplicationStore.getApplication(activity.application_id)
: activity.title && GameStore.getGameByName(activity.title);
});

const gameIcon = React.useMemo(() => game?.getIconURL(24), [game]);

const card = React.useMemo(
() => (
<UserProfileActivityCard
user={user}
currentUser={currentUser}
activity={activity}
className="allActivities-iconCard"
/>
),
[user, currentUser, activity, UserProfileActivityCard]
);

if (activity.name === "Spotify") {
return <ActivityIconIcon card={card} icon={SpotifyIcon} />;
} else if (activity.type === ActivityTypes.STREAMING) {
return <ActivityIconIcon card={card} icon={TwitchIcon} />;
} else if (activity.application_id && activity?.assets?.large_image) {
const icon = activity.assets.large_image.startsWith("mp:")
? activity.assets.large_image.replace("mp:", "https://media.discordapp.net/") + "?width=24&height=24"
: activity.assets.large_image.startsWith("spotify:")
? activity.assets.large_image.replace("spotify:", "https://i.scdn.co/image/")
: `https://cdn.discordapp.com/app-assets/${activity.application_id}/${activity.assets.large_image}.png?size=24`;
return <ActivityIconIcon card={card} icon={icon} />;
} else if (game && gameIcon) {
return <ActivityIconIcon card={card} icon={gameIcon} />;
}

return null;
}

export default function ActivityIcons({ user }: ActivityIconsProps) {
const currentUser = useStateFromStores([UserStore], () => UserStore.getCurrentUser());
const { live } = useUserProfileActivity(user.id);

return (
<div className="allActivities-icons">
{live.map((activity: any) => (
<ActivityIcon user={user} currentUser={currentUser} activity={activity} />
))}
</div>
);
}

0 comments on commit f3400f2

Please sign in to comment.