Plugin

WhosWatching

Lets you view what users are watching your stream by hovering over the screenshare icon

index.tsx
Download

Source

src/plugins/whosWatching/index.tsx
1/*
2 * FiveCord — a Discord client mod
3 * Copyright (c) 2025 FiveCord
4 * SPDX-License-Identifier: GPL-3.0-or-later
5 */
6
7import { classNameFactory } from "@api/Styles";
8import { Flex } from "@components/Flex";
9import { Devs } from "@utils/constants";
10import definePlugin from "@utils/types";
11import { findByPropsLazy, findStoreLazy } from "@webpack";
12import { Forms, i18n, React, RelationshipStore, Tooltip, UserStore, useStateFromStores } from "@webpack/common";
13
14interface WatchingProps {
15 userIds: string[];
16 guildId?: string;
17}
18
19const cl = classNameFactory("whosWatching-");
20
21function getUsername(user: any): string {
22 return RelationshipStore.getNickname(user.id) || user.globalName || user.username;
23}
24
25function Watching({ userIds, guildId }: WatchingProps): React.ReactNode {
26 // 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
27 let missingUsers = 0;
28 const users = userIds.map(id => UserStore.getUser(id)).filter(user => Boolean(user) ? true : (missingUsers += 1, false));
29 return (
30 <div className={cl("content")}>
31 {userIds.length ?
32 (<>
33 <Forms.FormTitle>{i18n.Messages.SPECTATORS.format({ numViewers: userIds.length })}</Forms.FormTitle>
34 <Flex flexDirection="column" style={{ gap: 6 }} >
35 {users.map(user => (
36 <Flex key={user.id} flexDirection="row" style={{ gap: 6, alignContent: "center" }} className={cl("user")} >
37 <img src={user.getAvatarURL(guildId)} style={{ borderRadius: 8, width: 16, height: 16 }} />
38 {getUsername(user)}
39 </Flex>
40 ))}
41 {missingUsers > 0 && <span className={cl("more_users")}>{`+${i18n.Messages.NUM_USERS.format({ num: missingUsers })}`}</span>}
42 </Flex>
43 </>)
44 : (<span className={cl("no_viewers")}>No spectators</span>)}
45 </div>
46 );
47}
48
49const ApplicationStreamingStore = findStoreLazy("ApplicationStreamingStore");
50const { encodeStreamKey }: {
51 encodeStreamKey: (any) => string;
52} = findByPropsLazy("encodeStreamKey");
53
54export default definePlugin({
55 name: "WhosWatching",
56 description: "Lets you view what users are watching your stream by hovering over the screenshare icon",
57 authors: [Devs.FiveCord],
58 patches: [
59 {
60 find: ".Masks.STATUS_SCREENSHARE,width:32",
61 replacement: {
62 match: /default:function\(\)\{return ([a-zA-Z0-9_]{0,5})\}/,
63 replace: "default:function(){return $self.component({OriginalComponent:$1})}"
64 }
65 }
66 ],
67 component: function ({ OriginalComponent }) {
68 return (props: any) => {
69 const stream = useStateFromStores([ApplicationStreamingStore], () => ApplicationStreamingStore.getCurrentUserActiveStream());
70 const viewers = ApplicationStreamingStore.getViewerIds(encodeStreamKey(stream));
71 return <Tooltip text={<Watching userIds={viewers} guildId={stream.guildId} />}>
72 {({ onMouseEnter, onMouseLeave }) => (
73 <div onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>
74 <OriginalComponent {...props} />
75 </div>
76 )}
77 </Tooltip>;
78 };
79 }
80});
81