mirror of
https://github.com/Equicord/Equicord.git
synced 2025-06-09 14:43:03 -04:00
Merge remote-tracking branch 'upstream/dev' into dev
This commit is contained in:
commit
ad61525ae6
6 changed files with 104 additions and 48 deletions
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "equicord",
|
"name": "equicord",
|
||||||
"private": "true",
|
"private": "true",
|
||||||
"version": "1.10.1",
|
"version": "1.10.2",
|
||||||
"description": "The other cutest Discord client mod",
|
"description": "The other cutest Discord client mod",
|
||||||
"homepage": "https://github.com/Equicord/Equicord#readme",
|
"homepage": "https://github.com/Equicord/Equicord#readme",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
|
|
|
@ -158,4 +158,5 @@ export const defaultRules = [
|
||||||
"igshid",
|
"igshid",
|
||||||
"igsh",
|
"igsh",
|
||||||
"share_id@reddit.com",
|
"share_id@reddit.com",
|
||||||
|
"si@soundcloud.com",
|
||||||
];
|
];
|
||||||
|
|
|
@ -112,12 +112,12 @@ export default definePlugin({
|
||||||
},
|
},
|
||||||
// patch request that queries if term is allowed
|
// patch request that queries if term is allowed
|
||||||
{
|
{
|
||||||
find: ".GUILD_DISCOVERY_VALID_TERM",
|
find: ".GUILD_DISCOVERY_VALID_TERM,query:",
|
||||||
predicate: () => settings.store.disableDisallowedDiscoveryFilters,
|
predicate: () => settings.store.disableDisallowedDiscoveryFilters,
|
||||||
all: true,
|
all: true,
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /\i\.\i\.get\(\{url:\i\.\i\.GUILD_DISCOVERY_VALID_TERM,query:\{term:\i\},oldFormErrors:!0\}\);/g,
|
match: /\i\.\i\.get\(\{url:\i\.\i\.GUILD_DISCOVERY_VALID_TERM,query:\{term:\i\},oldFormErrors:!0\}\)/g,
|
||||||
replace: "Promise.resolve({ body: { valid: true } });"
|
replace: "Promise.resolve({ body: { valid: true } })"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
@ -7,17 +7,24 @@
|
||||||
import { classNameFactory } from "@api/Styles";
|
import { classNameFactory } from "@api/Styles";
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { classes } from "@utils/misc";
|
import { classes } from "@utils/misc";
|
||||||
import { findByPropsLazy, findComponentByCodeLazy, findStoreLazy } from "@webpack";
|
import { filters, findByCodeLazy, findByPropsLazy, findComponentByCodeLazy, findStoreLazy, mapMangledModuleLazy } from "@webpack";
|
||||||
import { ChannelStore, GuildStore, IconUtils, NavigationRouter, PermissionsBits, PermissionStore, showToast, Text, Toasts, Tooltip, useCallback, useMemo, UserStore, useStateFromStores } from "@webpack/common";
|
import { ChannelStore, GuildStore, IconUtils, match, NavigationRouter, P, PermissionsBits, PermissionStore, React, showToast, Text, Toasts, Tooltip, useMemo, UserStore, useStateFromStores } from "@webpack/common";
|
||||||
import { Channel } from "discord-types/general";
|
import { Channel } from "discord-types/general";
|
||||||
|
|
||||||
const cl = classNameFactory("vc-uvs-");
|
const cl = classNameFactory("vc-uvs-");
|
||||||
|
|
||||||
const { selectVoiceChannel } = findByPropsLazy("selectChannel", "selectVoiceChannel");
|
const { selectVoiceChannel } = findByPropsLazy("selectVoiceChannel", "selectChannel");
|
||||||
|
const { useChannelName } = mapMangledModuleLazy(".Messages.GROUP_DM_ALONE", {
|
||||||
|
useChannelName: filters.byCode("()=>null==")
|
||||||
|
});
|
||||||
|
const getDMChannelIcon = findByCodeLazy(".getChannelIconURL({");
|
||||||
const VoiceStateStore = findStoreLazy("VoiceStateStore");
|
const VoiceStateStore = findStoreLazy("VoiceStateStore");
|
||||||
const UserSummaryItem = findComponentByCodeLazy("defaultRenderUser", "showDefaultAvatarsForNullUsers");
|
|
||||||
|
|
||||||
interface IconProps extends React.HTMLAttributes<HTMLDivElement> {
|
const UserSummaryItem = findComponentByCodeLazy("defaultRenderUser", "showDefaultAvatarsForNullUsers");
|
||||||
|
const Avatar = findComponentByCodeLazy(".AVATAR_STATUS_TYPING_16;");
|
||||||
|
const GroupDMAvatars = findComponentByCodeLazy(".AvatarSizeSpecs[", "getAvatarURL");
|
||||||
|
|
||||||
|
interface IconProps extends React.ComponentPropsWithoutRef<"div"> {
|
||||||
size?: number;
|
size?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +35,7 @@ function SpeakerIcon(props: IconProps) {
|
||||||
<div
|
<div
|
||||||
{...props}
|
{...props}
|
||||||
role={props.onClick != null ? "button" : undefined}
|
role={props.onClick != null ? "button" : undefined}
|
||||||
className={classes(cl("speaker"), props.onClick != null ? cl("clickable") : undefined)}
|
className={classes(cl("speaker"), props.onClick != null ? cl("clickable") : undefined, props.className)}
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
width={props.size}
|
width={props.size}
|
||||||
|
@ -50,7 +57,7 @@ function LockedSpeakerIcon(props: IconProps) {
|
||||||
<div
|
<div
|
||||||
{...props}
|
{...props}
|
||||||
role={props.onClick != null ? "button" : undefined}
|
role={props.onClick != null ? "button" : undefined}
|
||||||
className={classes(cl("speaker"), props.onClick != null ? cl("clickable") : undefined)}
|
className={classes(cl("speaker"), props.onClick != null ? cl("clickable") : undefined, props.className)}
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
width={props.size}
|
width={props.size}
|
||||||
|
@ -71,39 +78,46 @@ interface VoiceChannelTooltipProps {
|
||||||
|
|
||||||
function VoiceChannelTooltip({ channel }: VoiceChannelTooltipProps) {
|
function VoiceChannelTooltip({ channel }: VoiceChannelTooltipProps) {
|
||||||
const voiceStates = useStateFromStores([VoiceStateStore], () => VoiceStateStore.getVoiceStatesForChannel(channel.id));
|
const voiceStates = useStateFromStores([VoiceStateStore], () => VoiceStateStore.getVoiceStatesForChannel(channel.id));
|
||||||
|
|
||||||
const users = useMemo(
|
const users = useMemo(
|
||||||
() => Object.values<any>(voiceStates).map(voiceState => UserStore.getUser(voiceState.userId)).filter(user => user != null),
|
() => Object.values<any>(voiceStates).map(voiceState => UserStore.getUser(voiceState.userId)).filter(user => user != null),
|
||||||
[voiceStates]
|
[voiceStates]
|
||||||
);
|
);
|
||||||
|
|
||||||
const guild = useMemo(
|
const guild = channel.getGuildId() == null ? undefined : GuildStore.getGuild(channel.getGuildId());
|
||||||
() => channel.getGuildId() == null ? undefined : GuildStore.getGuild(channel.getGuildId()),
|
const guildIcon = guild?.icon == null ? undefined : IconUtils.getGuildIconURL({
|
||||||
[channel]
|
id: guild.id,
|
||||||
);
|
icon: guild.icon,
|
||||||
|
size: 30
|
||||||
|
});
|
||||||
|
|
||||||
const guildIcon = useMemo(() => {
|
const channelIcon = match(channel.type)
|
||||||
return guild?.icon == null ? undefined : IconUtils.getGuildIconURL({
|
.with(P.union(1, 3), () => {
|
||||||
id: guild.id,
|
return channel.recipients.length >= 2 && channel.icon == null
|
||||||
icon: guild.icon,
|
? <GroupDMAvatars recipients={channel.recipients} size="SIZE_32" />
|
||||||
size: 30
|
: <Avatar src={getDMChannelIcon(channel)} size="SIZE_32" />;
|
||||||
});
|
})
|
||||||
}, [guild]);
|
.otherwise(() => null);
|
||||||
|
const channelName = useChannelName(channel);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{guild != null && (
|
{guild != null && (
|
||||||
<div className={cl("guild-name")}>
|
<div className={cl("name")}>
|
||||||
{guildIcon != null && <img className={cl("guild-icon")} src={guildIcon} alt="" />}
|
{guildIcon != null && <img className={cl("guild-icon")} src={guildIcon} alt="" />}
|
||||||
<Text variant="text-sm/bold">{guild.name}</Text>
|
<Text variant="text-sm/bold">{guild.name}</Text>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<Text variant="text-sm/semibold">{channel.name}</Text>
|
<div className={cl("name")}>
|
||||||
|
{channelIcon}
|
||||||
|
<Text variant="text-sm/semibold">{channelName}</Text>
|
||||||
|
</div>
|
||||||
<div className={cl("vc-members")}>
|
<div className={cl("vc-members")}>
|
||||||
<SpeakerIcon size={18} />
|
<SpeakerIcon size={18} />
|
||||||
<UserSummaryItem
|
<UserSummaryItem
|
||||||
users={users}
|
users={users}
|
||||||
renderIcon={false}
|
renderIcon={false}
|
||||||
max={14}
|
max={13}
|
||||||
size={18}
|
size={18}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -113,20 +127,28 @@ function VoiceChannelTooltip({ channel }: VoiceChannelTooltipProps) {
|
||||||
|
|
||||||
interface VoiceChannelIndicatorProps {
|
interface VoiceChannelIndicatorProps {
|
||||||
userId: string;
|
userId: string;
|
||||||
|
isActionButton?: boolean;
|
||||||
|
isMessageIndicator?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const clickTimers = {} as Record<string, any>;
|
const clickTimers = {} as Record<string, any>;
|
||||||
|
|
||||||
export const VoiceChannelIndicator = ErrorBoundary.wrap(({ userId }: VoiceChannelIndicatorProps) => {
|
export const VoiceChannelIndicator = ErrorBoundary.wrap(({ userId, isActionButton, isMessageIndicator }: VoiceChannelIndicatorProps) => {
|
||||||
const channelId = useStateFromStores([VoiceStateStore], () => VoiceStateStore.getVoiceStateForUser(userId)?.channelId as string | undefined);
|
const channelId = useStateFromStores([VoiceStateStore], () => VoiceStateStore.getVoiceStateForUser(userId)?.channelId as string | undefined);
|
||||||
const channel = useMemo(() => channelId == null ? undefined : ChannelStore.getChannel(channelId), [channelId]);
|
|
||||||
|
|
||||||
const onClick = useCallback((e: React.MouseEvent) => {
|
const channel = channelId == null ? undefined : ChannelStore.getChannel(channelId);
|
||||||
|
if (channel == null) return null;
|
||||||
|
|
||||||
|
const isDM = channel.isDM() || channel.isMultiUserDM();
|
||||||
|
const isLocked = !isDM && (!PermissionStore.can(PermissionsBits.VIEW_CHANNEL, channel) || !PermissionStore.can(PermissionsBits.CONNECT, channel));
|
||||||
|
|
||||||
|
function onClick(e: React.MouseEvent) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
if (channel == null || channelId == null) return;
|
if (channel == null || channelId == null) return;
|
||||||
|
|
||||||
if (!PermissionStore.can(PermissionsBits.VIEW_CHANNEL, channel)) {
|
if (!isDM && !PermissionStore.can(PermissionsBits.VIEW_CHANNEL, channel)) {
|
||||||
showToast("You cannot view the user's Voice Channel", Toasts.Type.FAILURE);
|
showToast("You cannot view the user's Voice Channel", Toasts.Type.FAILURE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -135,7 +157,7 @@ export const VoiceChannelIndicator = ErrorBoundary.wrap(({ userId }: VoiceChanne
|
||||||
delete clickTimers[channelId];
|
delete clickTimers[channelId];
|
||||||
|
|
||||||
if (e.detail > 1) {
|
if (e.detail > 1) {
|
||||||
if (!PermissionStore.can(PermissionsBits.CONNECT, channel)) {
|
if (!isDM && !PermissionStore.can(PermissionsBits.CONNECT, channel)) {
|
||||||
showToast("You cannot join the user's Voice Channel", Toasts.Type.FAILURE);
|
showToast("You cannot join the user's Voice Channel", Toasts.Type.FAILURE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -147,13 +169,7 @@ export const VoiceChannelIndicator = ErrorBoundary.wrap(({ userId }: VoiceChanne
|
||||||
delete clickTimers[channelId];
|
delete clickTimers[channelId];
|
||||||
}, 250);
|
}, 250);
|
||||||
}
|
}
|
||||||
}, [channelId]);
|
}
|
||||||
|
|
||||||
const isLocked = useMemo(() => {
|
|
||||||
return !PermissionStore.can(PermissionsBits.VIEW_CHANNEL, channel) || !PermissionStore.can(PermissionsBits.CONNECT, channel);
|
|
||||||
}, [channelId]);
|
|
||||||
|
|
||||||
if (channel == null) return null;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
|
@ -161,11 +177,20 @@ export const VoiceChannelIndicator = ErrorBoundary.wrap(({ userId }: VoiceChanne
|
||||||
tooltipClassName={cl("tooltip-container")}
|
tooltipClassName={cl("tooltip-container")}
|
||||||
tooltipContentClassName={cl("tooltip-content")}
|
tooltipContentClassName={cl("tooltip-content")}
|
||||||
>
|
>
|
||||||
{props =>
|
{props => {
|
||||||
isLocked ?
|
const iconProps: IconProps = {
|
||||||
<LockedSpeakerIcon {...props} onClick={onClick} />
|
...props,
|
||||||
: <SpeakerIcon {...props} onClick={onClick} />
|
className: isActionButton ? cl("indicator-action-button") : cl("speaker-padding"),
|
||||||
}
|
size: isActionButton ? 20 : undefined,
|
||||||
|
onClick
|
||||||
|
};
|
||||||
|
|
||||||
|
return <div className={isMessageIndicator ? cl("message-indicator") : undefined}>
|
||||||
|
{isLocked ?
|
||||||
|
<LockedSpeakerIcon {...iconProps} />
|
||||||
|
: <SpeakerIcon {...iconProps} />}
|
||||||
|
</div>;
|
||||||
|
}}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
}, { noop: true });
|
}, { noop: true });
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
import "./style.css";
|
import "./style.css";
|
||||||
|
|
||||||
import { addDecorator, removeDecorator } from "@api/MemberListDecorators";
|
import { addDecorator, removeDecorator } from "@api/MemberListDecorators";
|
||||||
|
import { addDecoration, removeDecoration } from "@api/MessageDecorations";
|
||||||
import { definePluginSettings } from "@api/Settings";
|
import { definePluginSettings } from "@api/Settings";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
|
@ -37,13 +38,19 @@ const settings = definePluginSettings({
|
||||||
description: "Show a user's Voice Channel indicator in the member and DMs list",
|
description: "Show a user's Voice Channel indicator in the member and DMs list",
|
||||||
default: true,
|
default: true,
|
||||||
restartNeeded: true
|
restartNeeded: true
|
||||||
|
},
|
||||||
|
showInMessages: {
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
description: "Show a user's Voice Channel indicator in messages",
|
||||||
|
default: true,
|
||||||
|
restartNeeded: true
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "UserVoiceShow",
|
name: "UserVoiceShow",
|
||||||
description: "Shows an indicator when a user is in a Voice Channel",
|
description: "Shows an indicator when a user is in a Voice Channel",
|
||||||
authors: [Devs.LordElias, Devs.Nuckyz],
|
authors: [Devs.Nuckyz, Devs.LordElias],
|
||||||
settings,
|
settings,
|
||||||
|
|
||||||
patches: [
|
patches: [
|
||||||
|
@ -77,10 +84,10 @@ export default definePlugin({
|
||||||
}, */
|
}, */
|
||||||
// Friends List
|
// Friends List
|
||||||
{
|
{
|
||||||
find: ".avatar,animate:",
|
find: "null!=this.peopleListItemRef.current",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /\.subtext,children:.+?}\)\]}\)(?=])/,
|
match: /\.actions,children:\[/,
|
||||||
replace: "$&,$self.VoiceChannelIndicator({userId:arguments[0]?.user?.id})"
|
replace: "$&$self.VoiceChannelIndicator({userId:this?.props?.user?.id,isActionButton:true}),"
|
||||||
},
|
},
|
||||||
predicate: () => settings.store.showInMemberList
|
predicate: () => settings.store.showInMemberList
|
||||||
}
|
}
|
||||||
|
@ -90,10 +97,14 @@ export default definePlugin({
|
||||||
if (settings.store.showInMemberList) {
|
if (settings.store.showInMemberList) {
|
||||||
addDecorator("UserVoiceShow", ({ user }) => user == null ? null : <VoiceChannelIndicator userId={user.id} />);
|
addDecorator("UserVoiceShow", ({ user }) => user == null ? null : <VoiceChannelIndicator userId={user.id} />);
|
||||||
}
|
}
|
||||||
|
if (settings.store.showInMessages) {
|
||||||
|
addDecoration("UserVoiceShow", ({ message }) => message?.author == null ? null : <VoiceChannelIndicator userId={message.author.id} isMessageIndicator />);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
stop() {
|
stop() {
|
||||||
removeDecorator("UserVoiceShow");
|
removeDecorator("UserVoiceShow");
|
||||||
|
removeDecoration("UserVoiceShow");
|
||||||
},
|
},
|
||||||
|
|
||||||
VoiceChannelIndicator
|
VoiceChannelIndicator
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
.vc-uvs-speaker {
|
.vc-uvs-speaker {
|
||||||
color: var(--interactive-normal);
|
color: var(--interactive-normal);
|
||||||
padding: 0 4px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
@ -14,6 +13,26 @@
|
||||||
color: var(--interactive-hover);
|
color: var(--interactive-hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.vc-uvs-speaker-padding {
|
||||||
|
padding: 0 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-uvs-message-indicator {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
top: 2.5px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-uvs-indicator-action-button {
|
||||||
|
background-color: var(--background-secondary);
|
||||||
|
border-radius: 100%;
|
||||||
|
height: 36px;
|
||||||
|
width: 36px;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.vc-uvs-tooltip-container {
|
.vc-uvs-tooltip-container {
|
||||||
max-width: 300px;
|
max-width: 300px;
|
||||||
}
|
}
|
||||||
|
@ -24,7 +43,7 @@
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-uvs-guild-name {
|
.vc-uvs-name {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue