generated from moonlight-mod/sample-extension
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
b065ae7
commit f3400f2
Showing
5 changed files
with
243 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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_' | ||
] | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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} | ||
/> | ||
)) | ||
]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
); | ||
} |