|
| 1 | +import React from "@moonlight-mod/wp/react"; |
| 2 | +import spacepack from "@moonlight-mod/wp/spacepack_spacepack"; |
| 3 | + |
| 4 | +import { addItem, MenuItem, MenuSeparator } from "@moonlight-mod/wp/contextMenu_contextMenu"; |
| 5 | +import { ToastType, createToast, showToast } from "@moonlight-mod/wp/discord/components/common/index"; |
| 6 | +import { ChannelTypes, Permissions } from "@moonlight-mod/wp/discord/Constants"; |
| 7 | +import { GuildChannelStore, PermissionStore, ChannelListStore } from "@moonlight-mod/wp/common_stores"; |
| 8 | + |
| 9 | +const { has: hasFlag } = spacepack.require("discord/utils/BigFlagUtils"); |
| 10 | + |
| 11 | +const logger = moonlight.getLogger("Voice Text Link"); |
| 12 | + |
| 13 | +async function linkChannel(channelMap: Record<string, string>, voice: any, text: any) { |
| 14 | + try { |
| 15 | + channelMap[voice.id] = text.id; |
| 16 | + await moonlight.setConfigOption("voiceTextLink", "channelMap", channelMap); |
| 17 | + showToast(createToast(`Linked "${text.name}" to "${voice.name}"`, ToastType.SUCCESS)); |
| 18 | + } catch (err) { |
| 19 | + logger.error(`Failed to link "${text.name}" (${text.id}) to "${voice.name}" (${voice.id}):`, err); |
| 20 | + showToast(createToast("Failed to link text channel", ToastType.FAILURE)); |
| 21 | + } |
| 22 | +} |
| 23 | + |
| 24 | +addItem( |
| 25 | + "channel-context", |
| 26 | + ({ channel }: { channel: any }) => { |
| 27 | + const channelMap = moonlight.getConfigOption<Record<string, string>>("voiceTextLink", "channelMap") ?? {}; |
| 28 | + const linked = channelMap[channel.id] != null; |
| 29 | + |
| 30 | + const muted = ChannelListStore.getGuildWithoutChangingGuildActionRows(channel.guild_id)?.guildChannels |
| 31 | + ?.mutedChannelIds; |
| 32 | + |
| 33 | + const textChannels = |
| 34 | + GuildChannelStore.getChannels(channel.guild_id) |
| 35 | + ?.SELECTABLE?.map((c: any) => c.channel) |
| 36 | + ?.filter((c: any) => c.type === ChannelTypes.GUILD_TEXT) |
| 37 | + ?.filter((c: any) => hasFlag(PermissionStore.getChannelPermissions(c), Permissions.SEND_MESSAGES)) |
| 38 | + ?.filter((c: any) => !muted.has(c.id) && !muted.has(c.parent_id)) ?? []; |
| 39 | + const likelyChannels = textChannels.filter((c: any) => c.name.includes("voice") || c.name.includes("vc-")); |
| 40 | + const likelyIds = likelyChannels.map((c: any) => c.id); |
| 41 | + const otherChannels = textChannels.filter((c: any) => !likelyIds.includes(c.id)); |
| 42 | + |
| 43 | + return channel.guild_id == null || channel.type !== ChannelTypes.GUILD_VOICE ? null : ( |
| 44 | + <MenuItem |
| 45 | + id="voice-text-link" |
| 46 | + label={linked ? "Unlink Text Channel" : "Link To Text Channel"} |
| 47 | + action={ |
| 48 | + linked |
| 49 | + ? async () => { |
| 50 | + try { |
| 51 | + delete channelMap[channel.id]; |
| 52 | + await moonlight.setConfigOption("voiceTextLink", "channelMap", channelMap); |
| 53 | + showToast(createToast(`Unlinked text channel from "${channel.name}"`, ToastType.SUCCESS)); |
| 54 | + } catch (err) { |
| 55 | + logger.error(`Failed to unlink text channel from "${channel.name}" (${channel.id}):`, err); |
| 56 | + showToast(createToast("Failed to unlink text channel", ToastType.FAILURE)); |
| 57 | + } |
| 58 | + } |
| 59 | + : () => {} |
| 60 | + } |
| 61 | + > |
| 62 | + {linked ? null : likelyChannels.length > 0 ? ( |
| 63 | + <> |
| 64 | + {likelyChannels.map((c: any) => ( |
| 65 | + <MenuItem |
| 66 | + id={c.id} |
| 67 | + label={`#${c.name}`} |
| 68 | + action={async () => { |
| 69 | + await linkChannel(channelMap, channel, c); |
| 70 | + }} |
| 71 | + /> |
| 72 | + ))} |
| 73 | + <MenuSeparator /> |
| 74 | + <MenuItem id="others" label="Other Channels"> |
| 75 | + {otherChannels.map((c: any) => ( |
| 76 | + <MenuItem |
| 77 | + id={c.id} |
| 78 | + label={`#${c.name}`} |
| 79 | + action={async () => { |
| 80 | + await linkChannel(channelMap, channel, c); |
| 81 | + }} |
| 82 | + /> |
| 83 | + ))} |
| 84 | + </MenuItem> |
| 85 | + </> |
| 86 | + ) : ( |
| 87 | + textChannels.map((c: any) => ( |
| 88 | + <MenuItem |
| 89 | + id={c.id} |
| 90 | + label={`#${c.name}`} |
| 91 | + action={async () => { |
| 92 | + await linkChannel(channelMap, channel, c); |
| 93 | + }} |
| 94 | + /> |
| 95 | + )) |
| 96 | + )} |
| 97 | + </MenuItem> |
| 98 | + ); |
| 99 | + }, |
| 100 | + "mute-channel", |
| 101 | + true |
| 102 | +); |
0 commit comments