/*
* Vencord, a Discord client mod
* Copyright (c) 2024 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { definePluginSettings } from "@api/Settings";
import { classNameFactory } from "@api/Styles";
import ErrorBoundary from "@components/ErrorBoundary";
import { Flex } from "@components/Flex";
import { EquicordDevs } from "@utils/constants";
import { openUserProfile } from "@utils/discord";
import { Margins } from "@utils/margins";
import { classes } from "@utils/misc";
import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy, findComponentByCodeLazy, findStoreLazy } from "@webpack";
import { Clickable, Forms, i18n, RelationshipStore, Tooltip, UserStore, useStateFromStores } from "@webpack/common";
import { User } from "discord-types/general";
interface WatchingProps {
userIds: string[];
guildId?: string;
}
const cl = classNameFactory("whosWatching-");
function getUsername(user: any): string {
return RelationshipStore.getNickname(user.id) || user.globalName || user.username;
}
const settings = definePluginSettings({
showPanel: {
description: "Show spectators under screenshare panel",
type: OptionType.BOOLEAN,
default: true,
restartNeeded: true
},
});
function encodeStreamKey(stream) {
const { streamType, guildId, channelId, ownerId } = stream;
switch (streamType) {
case "guild":
return [streamType, guildId, channelId, ownerId].join(":");
case "call":
return [streamType, channelId, ownerId].join(":");
default:
throw console.log("Unknown stream type ".concat(streamType));
}
}
function Watching({ userIds, guildId }: WatchingProps): JSX.Element {
// Missing Users happen when UserStore.getUser(id) returns null -- The client should automatically cache spectators, so this might not be possible but it's better to be sure just in case
let missingUsers = 0;
const users = userIds.map(id => UserStore.getUser(id)).filter(user => Boolean(user) ? true : (missingUsers += 1, false));
return (
{userIds.length ?
(<>
{i18n.Messages.SPECTATORS.format({ numViewers: userIds.length })}
{users.map(user => (
{getUsername(user)}
))}
{missingUsers > 0 && {`+${i18n.Messages.NUM_USERS.format({ num: missingUsers })}`}}
>)
: (
No spectators)}
);
}
const ApplicationStreamingStore = findStoreLazy("ApplicationStreamingStore");
const UserSummaryItem = findComponentByCodeLazy("defaultRenderUser", "showDefaultAvatarsForNullUsers");
const AvatarStyles = findByPropsLazy("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar");
export default definePlugin({
name: "WhosWatching",
description: "Hover over the screenshare icon to view what users are watching your stream",
authors: [EquicordDevs.Fres],
settings: settings,
patches: [
{
find: ".Masks.STATUS_SCREENSHARE,width:32",
replacement: {
match: /(\i):function\(\)\{return (\i)\}/,
replace: "$1:function(){return $self.component({OriginalComponent:$2})}"
}
},
{
predicate: () => settings.store.showPanel,
find: "this.isJoinableActivity()||",
replacement: {
match: /(this\.isJoinableActivity\(\).{0,200}children:.{0,50})"div"/,
replace: "$1$self.WrapperComponent"
}
}
],
WrapperComponent: ErrorBoundary.wrap(props => {
const stream = useStateFromStores([ApplicationStreamingStore], () => ApplicationStreamingStore.getCurrentUserActiveStream());
if (!stream) return {props.children}
;
const userIds = ApplicationStreamingStore.getViewerIds(encodeStreamKey(stream));
let missingUsers = 0;
const users = userIds.map(id => UserStore.getUser(id)).filter(user => Boolean(user) ? true : (missingUsers += 1, false));
function renderMoreUsers(_label: string, count: number) {
const sliced = users.slice(count - 1);
return (
}>
{({ onMouseEnter, onMouseLeave }) => (
+{sliced.length + missingUsers}
)}
);
}
return (
<>
{props.children}
{users.length ?
<>
{i18n.Messages.SPECTATORS.format({ numViewers: userIds.length })}
(
openUserProfile(user.id)}
>
)}
/>
>
: No spectators
}
>
);
}),
component: function ({ OriginalComponent }) {
return ErrorBoundary.wrap((props: any) => {
const stream = useStateFromStores([ApplicationStreamingStore], () => ApplicationStreamingStore.getCurrentUserActiveStream());
const viewers = ApplicationStreamingStore.getViewerIds(encodeStreamKey(stream));
return }>
{({ onMouseEnter, onMouseLeave }) => (
)}
;
});
}
});