SilentTyping: Allow temporarily showing typing. (#1133)
* Allow temporarily showing typing. * Stuff. * Stuff.
This commit is contained in:
parent
bfba33956e
commit
c8faf15a72
1 changed files with 128 additions and 10 deletions
|
|
@ -23,9 +23,18 @@ import { plugins } from "@api/PluginManager";
|
||||||
import { definePluginSettings } from "@api/Settings";
|
import { definePluginSettings } from "@api/Settings";
|
||||||
import { openPluginModal } from "@components/settings";
|
import { openPluginModal } from "@components/settings";
|
||||||
import { Devs, EquicordDevs } from "@utils/constants";
|
import { Devs, EquicordDevs } from "@utils/constants";
|
||||||
|
import { useForceUpdater } from "@utils/react";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { Channel } from "@vencord/discord-types";
|
import { Channel } from "@vencord/discord-types";
|
||||||
import { ChannelStore, FluxDispatcher, Menu, React } from "@webpack/common";
|
import { ChannelStore, FluxDispatcher, Menu, MessageStore, React, SelectedChannelStore, useEffect, UserStore } from "@webpack/common";
|
||||||
|
|
||||||
|
const rerenderListeners = new Set<() => void>();
|
||||||
|
|
||||||
|
function triggerChatToggleRerender() {
|
||||||
|
for (const listener of rerenderListeners) {
|
||||||
|
listener();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const settings = definePluginSettings({
|
const settings = definePluginSettings({
|
||||||
enabledGlobally: {
|
enabledGlobally: {
|
||||||
|
|
@ -88,6 +97,21 @@ const settings = definePluginSettings({
|
||||||
description: "If enabled, the plugin will hide your typing from others in any DMs/channels/guilds not listed in \"Disabled Locations\" below. If disabled, the plugin will show your typing to others for any DMs/channels/guilds not listed in \"Enabled Locations\" below.",
|
description: "If enabled, the plugin will hide your typing from others in any DMs/channels/guilds not listed in \"Disabled Locations\" below. If disabled, the plugin will show your typing to others for any DMs/channels/guilds not listed in \"Enabled Locations\" below.",
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
|
alwaysEnableInActiveVoiceChat: {
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
description: "Always allow your typing indicator to show when typing in a voice channel you are connected to.",
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
temporaryEnableThresholdServers: {
|
||||||
|
type: OptionType.NUMBER,
|
||||||
|
description: "Temporarily allow your typing indicator to show for this many seconds after sending a message in a server channel. If the typing indicator is already visible in the channel, this setting will have no effect.",
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
temporaryEnableThresholdDirectMessages: {
|
||||||
|
type: OptionType.NUMBER,
|
||||||
|
description: "Temporarily allow your typing indicator to show for this many seconds after sending a message in a DM or Group DM. If the typing indicator is already visible in the channel, this setting will have no effect.",
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
enabledLocations: {
|
enabledLocations: {
|
||||||
type: OptionType.STRING,
|
type: OptionType.STRING,
|
||||||
description: "Enable functionality for these IDs. Accepts a comma separated list of DM IDs, channel IDs, and guild IDs. Only used if \"Default Hidden\" is disabled.",
|
description: "Enable functionality for these IDs. Accepts a comma separated list of DM IDs, channel IDs, and guild IDs. Only used if \"Default Hidden\" is disabled.",
|
||||||
|
|
@ -125,6 +149,9 @@ const SilentTypingChatToggle: ChatBarButtonFactory = ({ channel, type }) => {
|
||||||
defaultHidden,
|
defaultHidden,
|
||||||
enabledLocations,
|
enabledLocations,
|
||||||
disabledLocations,
|
disabledLocations,
|
||||||
|
alwaysEnableInActiveVoiceChat,
|
||||||
|
temporaryEnableThresholdServers,
|
||||||
|
temporaryEnableThresholdDirectMessages,
|
||||||
chatIconLeftClickAction,
|
chatIconLeftClickAction,
|
||||||
chatIconMiddleClickAction,
|
chatIconMiddleClickAction,
|
||||||
chatIconRightClickAction,
|
chatIconRightClickAction,
|
||||||
|
|
@ -134,21 +161,40 @@ const SilentTypingChatToggle: ChatBarButtonFactory = ({ channel, type }) => {
|
||||||
"defaultHidden",
|
"defaultHidden",
|
||||||
"enabledLocations",
|
"enabledLocations",
|
||||||
"disabledLocations",
|
"disabledLocations",
|
||||||
|
"alwaysEnableInActiveVoiceChat",
|
||||||
|
"temporaryEnableThresholdServers",
|
||||||
|
"temporaryEnableThresholdDirectMessages",
|
||||||
"chatIconLeftClickAction",
|
"chatIconLeftClickAction",
|
||||||
"chatIconMiddleClickAction",
|
"chatIconMiddleClickAction",
|
||||||
"chatIconRightClickAction",
|
"chatIconRightClickAction",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const forceUpdate = useForceUpdater();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
rerenderListeners.add(forceUpdate);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
rerenderListeners.delete(forceUpdate);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
const validChat = ["normal", "sidebar"].some(x => type.analyticsName === x);
|
const validChat = ["normal", "sidebar"].some(x => type.analyticsName === x);
|
||||||
|
|
||||||
if (!validChat || !chatIcon) return null;
|
if (!validChat || !chatIcon) return null;
|
||||||
|
|
||||||
const effectiveList = getEffectiveList();
|
const effectiveList = getEffectiveList();
|
||||||
const enabledLocally = enabledGlobally && checkEnabled(channel);
|
const isLocallyDisallowed = isExplicitlyDisallowed(channel);
|
||||||
|
const allowedLocallyImplicitly = isImplicitlyAllowed(channel);
|
||||||
const location = channel.guild_id && effectiveList.includes(channel.guild_id) ? "Guild" : effectiveList.includes(channel.id) ? "Channel" : "Global";
|
const location = channel.guild_id && effectiveList.includes(channel.guild_id) ? "Guild" : effectiveList.includes(channel.id) ? "Channel" : "Global";
|
||||||
|
|
||||||
const tooltip = enabledGlobally ? (
|
const tooltip = enabledGlobally ? (
|
||||||
enabledLocally ? `Typing Hidden (${location})` : `Typing Visible (${location})`
|
allowedLocallyImplicitly === 1
|
||||||
|
? "Typing Always Visible In The Connected Voice Channel Chat"
|
||||||
|
: allowedLocallyImplicitly === 2
|
||||||
|
? "Typing Temporarily Visible In This Channel Due To A Recent Message"
|
||||||
|
: isLocallyDisallowed
|
||||||
|
? `Typing Hidden (${location})` : `Typing Visible (${location})`
|
||||||
) : "Typing Visible (Global)";
|
) : "Typing Visible (Global)";
|
||||||
|
|
||||||
function performAction(action: string): void {
|
function performAction(action: string): void {
|
||||||
|
|
@ -188,7 +234,7 @@ const SilentTypingChatToggle: ChatBarButtonFactory = ({ channel, type }) => {
|
||||||
}}>
|
}}>
|
||||||
<svg width="20" height="20" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" style={{ scale: "1.2" }}>
|
<svg width="20" height="20" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" style={{ scale: "1.2" }}>
|
||||||
<path fill="currentColor" mask={`url(#silent-typing-msg-mask-${channel.id})`} d="M18.333 15.556H1.667a1.667 1.667 0 0 1 -1.667 -1.667v-10a1.667 1.667 0 0 1 1.667 -1.667h16.667a1.667 1.667 0 0 1 1.667 1.667v10a1.667 1.667 0 0 1 -1.667 1.667M4.444 6.25V4.861a0.417 0.417 0 0 0 -0.417 -0.417H2.639a0.417 0.417 0 0 0 -0.417 0.417V6.25a0.417 0.417 0 0 0 0.417 0.417h1.389a0.417 0.417 0 0 0 0.417 -0.417m3.333 0V4.861a0.417 0.417 0 0 0 -0.417 -0.417H5.973a0.417 0.417 0 0 0 -0.417 0.417V6.25a0.417 0.417 0 0 0 0.417 0.417h1.389a0.417 0.417 0 0 0 0.417 -0.417m3.333 0V4.861a0.417 0.417 0 0 0 -0.417 -0.417h-1.389a0.417 0.417 0 0 0 -0.417 0.417V6.25a0.417 0.417 0 0 0 0.417 0.417h1.389a0.417 0.417 0 0 0 0.417 -0.417m3.333 0V4.861a0.417 0.417 0 0 0 -0.417 -0.417h-1.389a0.417 0.417 0 0 0 -0.417 0.417V6.25a0.417 0.417 0 0 0 0.417 0.417h1.389a0.417 0.417 0 0 0 0.417 -0.417m3.333 0V4.861a0.417 0.417 0 0 0 -0.417 -0.417h-1.389a0.417 0.417 0 0 0 -0.417 0.417V6.25a0.417 0.417 0 0 0 0.417 0.417h1.389a0.417 0.417 0 0 0 0.417 -0.417m-11.667 3.333V8.194a0.417 0.417 0 0 0 -0.417 -0.417H4.306a0.417 0.417 0 0 0 -0.417 0.417V9.583a0.417 0.417 0 0 0 0.417 0.417h1.389a0.417 0.417 0 0 0 0.417 -0.417m3.333 0V8.194a0.417 0.417 0 0 0 -0.417 -0.417H7.639a0.417 0.417 0 0 0 -0.417 0.417V9.583a0.417 0.417 0 0 0 0.417 0.417h1.389a0.417 0.417 0 0 0 0.417 -0.417m3.333 0V8.194a0.417 0.417 0 0 0 -0.417 -0.417h-1.389a0.417 0.417 0 0 0 -0.417 0.417V9.583a0.417 0.417 0 0 0 0.417 0.417h1.389a0.417 0.417 0 0 0 0.417 -0.417m3.333 0V8.194a0.417 0.417 0 0 0 -0.417 -0.417h-1.389a0.417 0.417 0 0 0 -0.417 0.417V9.583a0.417 0.417 0 0 0 0.417 0.417h1.389a0.417 0.417 0 0 0 0.417 -0.417m-11.667 3.333v-1.389a0.417 0.417 0 0 0 -0.417 -0.417H2.639a0.417 0.417 0 0 0 -0.417 0.417V12.917a0.417 0.417 0 0 0 0.417 0.417h1.389a0.417 0.417 0 0 0 0.417 -0.417m10 0v-1.389a0.417 0.417 0 0 0 -0.417 -0.417H5.973a0.417 0.417 0 0 0 -0.417 0.417V12.917a0.417 0.417 0 0 0 0.417 0.417h8.056a0.417 0.417 0 0 0 0.417 -0.417m3.333 0v-1.389a0.417 0.417 0 0 0 -0.417 -0.417h-1.389a0.417 0.417 0 0 0 -0.417 0.417V12.917a0.417 0.417 0 0 0 0.417 0.417h1.389a0.417 0.417 0 0 0 0.417 -0.417" transform="translate(2, 3)" />
|
<path fill="currentColor" mask={`url(#silent-typing-msg-mask-${channel.id})`} d="M18.333 15.556H1.667a1.667 1.667 0 0 1 -1.667 -1.667v-10a1.667 1.667 0 0 1 1.667 -1.667h16.667a1.667 1.667 0 0 1 1.667 1.667v10a1.667 1.667 0 0 1 -1.667 1.667M4.444 6.25V4.861a0.417 0.417 0 0 0 -0.417 -0.417H2.639a0.417 0.417 0 0 0 -0.417 0.417V6.25a0.417 0.417 0 0 0 0.417 0.417h1.389a0.417 0.417 0 0 0 0.417 -0.417m3.333 0V4.861a0.417 0.417 0 0 0 -0.417 -0.417H5.973a0.417 0.417 0 0 0 -0.417 0.417V6.25a0.417 0.417 0 0 0 0.417 0.417h1.389a0.417 0.417 0 0 0 0.417 -0.417m3.333 0V4.861a0.417 0.417 0 0 0 -0.417 -0.417h-1.389a0.417 0.417 0 0 0 -0.417 0.417V6.25a0.417 0.417 0 0 0 0.417 0.417h1.389a0.417 0.417 0 0 0 0.417 -0.417m3.333 0V4.861a0.417 0.417 0 0 0 -0.417 -0.417h-1.389a0.417 0.417 0 0 0 -0.417 0.417V6.25a0.417 0.417 0 0 0 0.417 0.417h1.389a0.417 0.417 0 0 0 0.417 -0.417m3.333 0V4.861a0.417 0.417 0 0 0 -0.417 -0.417h-1.389a0.417 0.417 0 0 0 -0.417 0.417V6.25a0.417 0.417 0 0 0 0.417 0.417h1.389a0.417 0.417 0 0 0 0.417 -0.417m-11.667 3.333V8.194a0.417 0.417 0 0 0 -0.417 -0.417H4.306a0.417 0.417 0 0 0 -0.417 0.417V9.583a0.417 0.417 0 0 0 0.417 0.417h1.389a0.417 0.417 0 0 0 0.417 -0.417m3.333 0V8.194a0.417 0.417 0 0 0 -0.417 -0.417H7.639a0.417 0.417 0 0 0 -0.417 0.417V9.583a0.417 0.417 0 0 0 0.417 0.417h1.389a0.417 0.417 0 0 0 0.417 -0.417m3.333 0V8.194a0.417 0.417 0 0 0 -0.417 -0.417h-1.389a0.417 0.417 0 0 0 -0.417 0.417V9.583a0.417 0.417 0 0 0 0.417 0.417h1.389a0.417 0.417 0 0 0 0.417 -0.417m3.333 0V8.194a0.417 0.417 0 0 0 -0.417 -0.417h-1.389a0.417 0.417 0 0 0 -0.417 0.417V9.583a0.417 0.417 0 0 0 0.417 0.417h1.389a0.417 0.417 0 0 0 0.417 -0.417m-11.667 3.333v-1.389a0.417 0.417 0 0 0 -0.417 -0.417H2.639a0.417 0.417 0 0 0 -0.417 0.417V12.917a0.417 0.417 0 0 0 0.417 0.417h1.389a0.417 0.417 0 0 0 0.417 -0.417m10 0v-1.389a0.417 0.417 0 0 0 -0.417 -0.417H5.973a0.417 0.417 0 0 0 -0.417 0.417V12.917a0.417 0.417 0 0 0 0.417 0.417h8.056a0.417 0.417 0 0 0 0.417 -0.417m3.333 0v-1.389a0.417 0.417 0 0 0 -0.417 -0.417h-1.389a0.417 0.417 0 0 0 -0.417 0.417V12.917a0.417 0.417 0 0 0 0.417 0.417h1.389a0.417 0.417 0 0 0 0.417 -0.417" transform="translate(2, 3)" />
|
||||||
{(enabledLocally) && (
|
{(isLocallyDisallowed && !allowedLocallyImplicitly) && (
|
||||||
<>
|
<>
|
||||||
<mask id={`silent-typing-msg-mask-${channel.id}`}>
|
<mask id={`silent-typing-msg-mask-${channel.id}`}>
|
||||||
<path fill="#fff" d="M0 0h24v24H0Z"></path>
|
<path fill="#fff" d="M0 0h24v24H0Z"></path>
|
||||||
|
|
@ -228,17 +274,47 @@ function getEffectiveList(): string[] {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkEnabled(channel: string | Channel): boolean {
|
function isImplicitlyAllowed(channel: string | Channel): number {
|
||||||
|
const resolvedChannel = typeof channel === "string" ? ChannelStore.getChannel(channel) : channel;
|
||||||
|
|
||||||
|
if (!resolvedChannel) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const connectedVoiceChannelID = SelectedChannelStore.getVoiceChannelId();
|
||||||
|
const guildID = resolvedChannel.guild_id;
|
||||||
|
|
||||||
|
if (settings.store.alwaysEnableInActiveVoiceChat && connectedVoiceChannelID === resolvedChannel.id) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const lastMessage = MessageStore.getLastEditableMessage(resolvedChannel.id);
|
||||||
|
const messageTimestamp = lastMessage?.timestamp?.getTime?.();
|
||||||
|
|
||||||
|
if (messageTimestamp) {
|
||||||
|
const addedThreshold = !guildID
|
||||||
|
? settings.store.temporaryEnableThresholdDirectMessages
|
||||||
|
: settings.store.temporaryEnableThresholdServers;
|
||||||
|
|
||||||
|
if (Date.now() < messageTimestamp + (addedThreshold * 1000)) {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isExplicitlyDisallowed(channel: string | Channel): boolean {
|
||||||
if (!settings.store.enabledGlobally) return false;
|
if (!settings.store.enabledGlobally) return false;
|
||||||
|
|
||||||
const channelId = typeof channel === "string" ? channel : channel.id;
|
const channelID = typeof channel === "string" ? channel : channel.id;
|
||||||
const guildId = typeof channel === "string" ? ChannelStore.getChannel(channelId)?.guild_id : channel.guild_id;
|
const guildID = typeof channel === "string" ? ChannelStore.getChannel(channelID)?.guild_id : channel.guild_id;
|
||||||
const effectiveChannels = getEffectiveList();
|
const effectiveChannels = getEffectiveList();
|
||||||
|
|
||||||
if (settings.store.defaultHidden) {
|
if (settings.store.defaultHidden) {
|
||||||
return !effectiveChannels.includes(guildId) && !effectiveChannels.includes(channelId);
|
return !effectiveChannels.includes(guildID) && !effectiveChannels.includes(channelID);
|
||||||
} else {
|
} else {
|
||||||
return effectiveChannels.includes(guildId) || effectiveChannels.includes(channelId);
|
return effectiveChannels.includes(guildID) || effectiveChannels.includes(channelID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -308,11 +384,50 @@ export default definePlugin({
|
||||||
contextMenus: {
|
contextMenus: {
|
||||||
"textarea-context": ChatBarContextCheckbox
|
"textarea-context": ChatBarContextCheckbox
|
||||||
},
|
},
|
||||||
|
|
||||||
chatBarButton: {
|
chatBarButton: {
|
||||||
icon: SilentTypingChatIcon,
|
icon: SilentTypingChatIcon,
|
||||||
render: SilentTypingChatToggle
|
render: SilentTypingChatToggle
|
||||||
},
|
},
|
||||||
|
|
||||||
|
flux: {
|
||||||
|
VOICE_STATE_UPDATES({ voiceStates }) {
|
||||||
|
const state = voiceStates?.[0];
|
||||||
|
|
||||||
|
if (!state) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.userId !== UserStore.getCurrentUser().id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.channelId === state.oldChannelId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings.store.alwaysEnableInActiveVoiceChat) {
|
||||||
|
triggerChatToggleRerender();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
MESSAGE_CREATE({ message }) {
|
||||||
|
if (message.author.id === UserStore.getCurrentUser().id) {
|
||||||
|
const threshold = message.guild_id
|
||||||
|
? settings.store.temporaryEnableThresholdServers
|
||||||
|
: settings.store.temporaryEnableThresholdDirectMessages;
|
||||||
|
|
||||||
|
if (threshold > 0) {
|
||||||
|
triggerChatToggleRerender();
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
triggerChatToggleRerender();
|
||||||
|
}, (threshold * 1000) + 25);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: '.dispatch({type:"TYPING_START_LOCAL"',
|
find: '.dispatch({type:"TYPING_START_LOCAL"',
|
||||||
|
|
@ -438,7 +553,10 @@ export default definePlugin({
|
||||||
],
|
],
|
||||||
|
|
||||||
async startTyping(channelId: string) {
|
async startTyping(channelId: string) {
|
||||||
if (checkEnabled(channelId)) return;
|
if (isExplicitlyDisallowed(channelId) && !isImplicitlyAllowed(channelId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
FluxDispatcher.dispatch({ type: "TYPING_START_LOCAL", channelId });
|
FluxDispatcher.dispatch({ type: "TYPING_START_LOCAL", channelId });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue