Skip to content

Commit

Permalink
platform icons and unminify react errors
Browse files Browse the repository at this point in the history
  • Loading branch information
Cynosphere committed Jan 5, 2025
1 parent 2d37b07 commit ace6d64
Show file tree
Hide file tree
Showing 8 changed files with 755 additions and 0 deletions.
80 changes: 80 additions & 0 deletions src/platformIcons/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { Patch, ExtensionWebpackModule } from "@moonlight-mod/types";

export const patches: Patch[] = [
// Messages
// TODO: probably api-ify this
{
find: '},"new-member")),',
replace: {
match: /(?<=(\(0,.\.jsx\)).+?)\.BADGES]=(.);/,
replacement: (_, createElement, badges) =>
`.BADGES]=[${createElement}(require("platformIcons_icons").default,{user:arguments[0].message.author,extraClasses:["platform-icons-message"],size:"sm"}),...${badges}];`
}
},

// Member list
// TODO: api-ify
{
find: ".lostPermission",
replace: {
match: /(\(0,.\.jsxs\))\(.\.Fragment,{children:\[.{1,2}\(\),/,
replacement: (orig: string, createElement) =>
`${orig}${createElement}(require("platformIcons_icons").default,{user:arguments[0].user,extraClasses:["platform-icons-member-list"]}),`
}
},

// DM list
// TODO: api-ify
{
find: ".interactiveSystemDM]:",
replace: {
match: /decorators:(.\.isSystemDM\(\)\?(\(0,.\.jsx\))\(.+?verified:!0}\):null)/,
replacement: (_, orig, createElement) =>
`decorators:[${orig},${createElement}(require("platformIcons_icons").default,{user:arguments[0].user,extraClasses:["platform-icons-private-message"]})]`
}
},

// Profile
// TODO: api-ify
{
find: ".userTagDiscriminator,hideBotTag:!0",
replace: {
match: /,(\(0,.\.jsx\))\(.\.ZP,{userId:(.)\.id,.+?(.)\.clanTag}\),/,
replacement: (orig: string, createElement, user, ProfileClasses) =>
`${orig}${createElement}(require("platformIcons_icons").default,{user:${user},extraClasses:["platform-icons-profile", ${ProfileClasses}.clanTagContainer, ${ProfileClasses}.clanTag]}),`
}
},

// Voice icons
{
find: ".listCollapse",
replace: {
match: /(?<=(\(0,.\.jsxs\))\("div",{className:.\.iconGroup,onMouseEnter:.+?\(!1\)),children:\[/,
replacement: (_, createElement) =>
`,children:[${createElement}(require("platformIcons_voice").default,arguments[0]),`
}
}
];

export const webpackModules: Record<string, ExtensionWebpackModule> = {
icons: {
dependencies: [
{ ext: "spacepack", id: "spacepack" },
{ id: "react" },
{ id: "discord/packages/flux" },
{ id: "discord/components/common/index" },
{ ext: "common", id: "stores" },
"humanizeStatus:"
]
},
voice: {
dependencies: [
{ ext: "spacepack", id: "spacepack" },
{ id: "react" },
{ id: "discord/packages/flux" },
{ id: "discord/components/common/index" },
{ ext: "common", id: "stores" },
'.PLAYSTATION=3]="PLAYSTATION"'
]
}
};
39 changes: 39 additions & 0 deletions src/platformIcons/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
"id": "platformIcons",
"version": "1.0.0",
"meta": {
"name": "Platform Icons",
"tagline": "Shows what platform(s) a user is on",
"authors": [
"Cynosphere"
],
"tags": [
"qol",
"chat"
],
"source": "https://github.com/Cynosphere/moonlight-extensions"
},
"dependencies": [
"spacepack"
],
"settings": {
"self": {
"displayName": "Show own platforms",
"type": "boolean",
"default": true
},
"bots": {
"displayName": "Show bot platforms",
"type": "boolean",
"default": false
},
"voice": {
"displayName": "Show non-desktop platforms for voice users in channel list",
"description": "Only shows when connected to voice, as the data is only sent through voice",
"type": "boolean",
"default": true
}
},
"apiLevel": 2
}
20 changes: 20 additions & 0 deletions src/platformIcons/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.platform-icons-wrapper {
display: inline-block;
object-fit: contain;
margin-left: 0.25rem;
vertical-align: top;
position: relative;
top: 1px;
}

.platform-icons-profile {
display: flex;
flex-wrap: wrap;
align-items: center;
box-sizing: border-box;
width: fit-content;
gap: 2px;
margin: 1px 0;
padding: 0 2px;
top: 0;
}
92 changes: 92 additions & 0 deletions src/platformIcons/webpackModules/icons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import React from "@moonlight-mod/wp/react";
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
import { useStateFromStores } from "@moonlight-mod/wp/discord/packages/flux";

import { AuthenticationStore, PresenceStore, SessionsStore } from "@moonlight-mod/wp/common_stores";
import * as Components from "@moonlight-mod/wp/discord/components/common/index";

const { Tooltip, ScreenIcon, MobilePhoneIcon, GlobeEarthIcon, GameControllerIcon } = Components;

const IconsForPlatform = {
desktop: ScreenIcon,
mobile: MobilePhoneIcon,
web: GlobeEarthIcon,
embedded: GameControllerIcon
};

const { humanizeStatus } = spacepack.findByExports("humanizeStatus")[0].exports.ZP;

const StatusColors = {
online: "var(--green-360, var(--status-green-600))",
idle: "var(--yellow-300, var(--status-yellow-500))",
dnd: "var(--red-400, var(--status-red-500))"
};

type IconsProps = {
user: any;
extraClasses: string[];
size: "xxs" | "xs" | "sm" | "md" | "lg" | "custom" | "refresh_sm";
width?: number;
height?: number;
};

type Session = {
clientInfo: {
client: "desktop" | "mobile" | "web" | "embedded" | "unknown";
};
status: "online" | "idle" | "dnd" | "offline" | "invisible";
};

export default function PlatformIcons({ user, extraClasses, size = "xs", width, height }: IconsProps) {
const bots = moonlight.getConfigOption<Boolean>("platformIcons", "bots") ?? false;
const self = moonlight.getConfigOption<Boolean>("platformIcons", "self") ?? true;

const platforms = useStateFromStores(
[AuthenticationStore, PresenceStore, SessionsStore],
() =>
self && AuthenticationStore.getId() === user?.id
? Object.values(SessionsStore.getSessions())
.filter((session) => (session as Session).clientInfo.client !== "unknown")
.map((session) => ({ [(session as Session).clientInfo.client]: (session as Session).status }))
.reduce((obj, item) => Object.assign(obj, item), {})
: (PresenceStore.getState()?.clientStatuses?.[user?.id] ?? {}),
[user, self]
);

const elements = React.useMemo(() => {
const elements = [];

for (const platform of Object.keys(platforms)) {
const status = platforms[platform];

const props = {
text: `${humanizeStatus(status, false)} on ${platform.charAt(0).toUpperCase()}${platform.slice(1)}`,
key: `platform-icons-tooltip-${platform}`
};
// @ts-expect-error
const Icon = IconsForPlatform[platform];

elements.push(
<Tooltip {...props}>
{(tooltipProps: any) => (
<Icon
{...tooltipProps}
/* @ts-expect-error */
color={StatusColors[status]}
size={size}
width={width}
height={height}
key={`platform-icons-icon-${platform}`}
/>
)}
</Tooltip>
);
}

return elements;
}, [platforms]);

return (!bots && user.bot) || elements.length == 0 ? null : (
<div className={["platform-icons-wrapper", ...extraClasses].join(" ")}>{elements}</div>
);
}
51 changes: 51 additions & 0 deletions src/platformIcons/webpackModules/voice.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";
import { useStateFromStores } from "@moonlight-mod/wp/discord/packages/flux";

import { VoiceStateStore } from "@moonlight-mod/wp/common_stores";
import * as Components from "@moonlight-mod/wp/discord/components/common/index";

const { Tooltip, MobilePhoneIcon, GameControllerIcon } = Components;

const VoicePlatforms = spacepack.findObjectFromKey(
spacepack.findByCode('.PLAYSTATION=3]="PLAYSTATION"')[0].exports,
"PLAYSTATION"
);

const PlatformNames = {
[VoicePlatforms.MOBILE]: "mobile",
[VoicePlatforms.PLAYSTATION]: "PlayStation",
[VoicePlatforms.XBOX]: "Xbox"
};

const VoiceClasses = spacepack.findByCode("iconPriortySpeakerSpeaking:")[0].exports;

type PlatformIconVoiceProps = {
channelId: string;
user: any;
};

export default function PlatformIconVoice({ channelId, user }: PlatformIconVoiceProps) {
const enabled = moonlight.getConfigOption("platformIcons", "voice") ?? true;

const platform = useStateFromStores(
[VoiceStateStore],
() => VoiceStateStore.getVoicePlatformForChannel(channelId, user.id),
[channelId, user]
);

const Icon = React.useMemo(
() => (platform == VoicePlatforms.MOBILE ? MobilePhoneIcon : GameControllerIcon),
[platform]
);
const tooltipText = React.useMemo(() => `Connected via ${PlatformNames[platform]}`, [platform]);

// @ts-expect-error only not inlining this so i dont suppress all errors on the line
const isOverlay = window.__OVERLAY__;

return isOverlay || !enabled || !platform || platform === VoicePlatforms.DESKTOP ? null : (
<Tooltip text={tooltipText}>
{(tooltipProps: any) => <Icon {...tooltipProps} className={VoiceClasses.icon} color="currentColor" />}
</Tooltip>
);
}
Loading

0 comments on commit ace6d64

Please sign in to comment.