From e9665e0af1ff58344f69524a0dec9bdfb4d8ac64 Mon Sep 17 00:00:00 2001 From: Meryl Dakin Date: Tue, 20 Feb 2024 15:57:39 -0500 Subject: [PATCH] feat(kno-5254): hide inaccessible channels --- .../SlackChannelCombobox.tsx | 58 ++++++++++++------- .../SlackChannelOption.tsx | 20 ++++--- .../icons/CheckmarkIcon.tsx | 15 ++++- .../SlackChannelCombobox/styles.css | 19 ++++-- 4 files changed, 74 insertions(+), 38 deletions(-) diff --git a/packages/react/src/modules/slack/components/SlackChannelCombobox/SlackChannelCombobox.tsx b/packages/react/src/modules/slack/components/SlackChannelCombobox/SlackChannelCombobox.tsx index 6885e802..ef646b31 100644 --- a/packages/react/src/modules/slack/components/SlackChannelCombobox/SlackChannelCombobox.tsx +++ b/packages/react/src/modules/slack/components/SlackChannelCombobox/SlackChannelCombobox.tsx @@ -57,6 +57,13 @@ export const SlackChannelCombobox = ({ channelOptionProps, inputMessages, }: Props) => { + const [comboboxListOpen, setComboboxListOpen] = useState(false); + const [inputValue, setInputValue] = useState(""); + + // Used to close the combobox when clicking outside of it + const comboboxRef = useRef(null); + useOutsideClick({ ref: comboboxRef, fn: () => setComboboxListOpen(false) }); + // Gather API data const { connectionStatus, errorLabel: connectionErrorLabel } = useKnockSlackClient(); @@ -72,21 +79,25 @@ export const SlackChannelCombobox = ({ updating: connectedChannelsUpdating, } = useConnectedSlackChannels({ slackChannelsRecipientObject }); - const [comboboxListOpen, setComboboxListOpen] = useState(false); - const [inputValue, setInputValue] = useState(""); - - // Used to close the combobox when clicking outside of it - const comboboxRef = useRef(null); - useOutsideClick({ ref: comboboxRef, fn: () => setComboboxListOpen(false) }); - - // Used to optimistically show when user toggles a channel connected or not const [currentConnectedChannels, setCurrentConnectedChannels] = useState< SlackChannelConnection[] | null >(null); useEffect(() => { - setCurrentConnectedChannels(connectedChannels); - }, [connectedChannels]); + // Used to make sure we're only showing currently available channels to select from. + // There are cases where a channel is "connected" in Knock, but it wouldn't be + // posting to it if the channel is private and the Slackbot doesn't belong to it, + // so the channel won't show up here and it won't be posted to. + const slackChannelsMap = new Map( + slackChannels.map((channel) => [channel.id, channel]), + ); + + const channels = connectedChannels?.filter((connectedChannel) => { + return slackChannelsMap.has(connectedChannel.channel_id); + }) || []; + + setCurrentConnectedChannels(channels); + }, [connectedChannels, slackChannels]); const inErrorState = useMemo( () => @@ -124,26 +135,27 @@ export const SlackChannelCombobox = ({ ); } - const numberConnectedChannels = connectedChannels?.length || 0; + const numberConnectedChannels = currentConnectedChannels?.length || 0; - if (connectedChannels && numberConnectedChannels === 0) { + if (currentConnectedChannels && numberConnectedChannels === 0) { return ( inputMessages?.noChannelsConnected || DEFAULT_INPUT_MESSAGES.noChannelsConnected ); } - if (connectedChannels && numberConnectedChannels === 1) { + if (currentConnectedChannels && numberConnectedChannels === 1) { const connectedChannel = slackChannels?.find( - (slackChannel) => slackChannel.id === connectedChannels[0]?.channel_id, + (slackChannel) => + slackChannel.id === currentConnectedChannels[0]?.channel_id, ); return ( - inputMessages?.singleChannelConnected || `#${connectedChannel?.name}` + inputMessages?.singleChannelConnected || `# ${connectedChannel?.name}` ); } - if (connectedChannels && numberConnectedChannels > 1) { + if (currentConnectedChannels && numberConnectedChannels > 1) { return ( inputMessages?.multipleChannelsConnected || `${numberConnectedChannels} channels connected` @@ -152,35 +164,37 @@ export const SlackChannelCombobox = ({ return ""; }, [ - inLoadingState, connectionStatus, + inLoadingState, slackChannels, - connectedChannels, + currentConnectedChannels, inputMessages, connectionErrorLabel, ]); // Handle channel click const handleOptionClick = async (channelId: string) => { - if (!connectedChannels) { + if (!currentConnectedChannels) { return; } - const isChannelConnected = connectedChannels.find( + const isChannelConnected = currentConnectedChannels.find( (channel) => channel.channel_id === channelId, ); if (isChannelConnected) { - const channelsToSendToKnock = connectedChannels.filter( + const channelsToSendToKnock = currentConnectedChannels.filter( (connectedChannel) => connectedChannel.channel_id !== channelId, ); + setCurrentConnectedChannels(channelsToSendToKnock); updateConnectedChannels(channelsToSendToKnock); } else { const channelsToSendToKnock = [ - ...connectedChannels, + ...currentConnectedChannels, { channel_id: channelId } as SlackChannelConnection, ]; + setCurrentConnectedChannels(channelsToSendToKnock); updateConnectedChannels(channelsToSendToKnock); } diff --git a/packages/react/src/modules/slack/components/SlackChannelCombobox/SlackChannelOption.tsx b/packages/react/src/modules/slack/components/SlackChannelCombobox/SlackChannelOption.tsx index d4c9fa7b..e9e8c5d7 100644 --- a/packages/react/src/modules/slack/components/SlackChannelCombobox/SlackChannelOption.tsx +++ b/packages/react/src/modules/slack/components/SlackChannelCombobox/SlackChannelOption.tsx @@ -31,15 +31,15 @@ const SlackChannelOption = ({ const [submittedId, setSubmittedId] = useState(null); const icon = () => { - if (submittedId === channel.id && isUpdating) { - return ; + if (submittedId === channel.id && (isUpdating || isLoading)) { + return ; } if (isHovered || isConnected) { return ; } - return
; + return
; }; const handleOptionClick = (channelId: string) => { @@ -51,12 +51,12 @@ const SlackChannelOption = ({ if (submittedId && !isUpdating) { return setSubmittedId(null); } - }, [isUpdating, submittedId]); + }, [isLoading, isUpdating, submittedId]); return ( ); diff --git a/packages/react/src/modules/slack/components/SlackChannelCombobox/icons/CheckmarkIcon.tsx b/packages/react/src/modules/slack/components/SlackChannelCombobox/icons/CheckmarkIcon.tsx index ae72b849..4e8b1d18 100644 --- a/packages/react/src/modules/slack/components/SlackChannelCombobox/icons/CheckmarkIcon.tsx +++ b/packages/react/src/modules/slack/components/SlackChannelCombobox/icons/CheckmarkIcon.tsx @@ -1,10 +1,19 @@ -const CheckmarkIcon = ({ isConnected }: { isConnected: boolean }) => ( +const CheckmarkIcon = ({ + isConnected, + size = "1rem", + ...props +}: { + isConnected: boolean; + size?: string; +}) => (