From 4af2ad2f6989c625cc09624bab7b0d11b2061365 Mon Sep 17 00:00:00 2001 From: harshil Sharma Date: Wed, 15 Nov 2023 14:13:55 +0530 Subject: [PATCH] review fixes --- app/actions/remote/channel.ts | 2 +- app/components/button/index.tsx | 28 +++++--- .../convert_to_channel_label.tsx | 6 +- .../convert_gm_to_channel.tsx | 70 ++++++++++--------- .../convert_gm_to_channel_form.tsx | 36 ++++++++-- .../convert_gm_to_channel/team_selector.tsx | 19 ++--- .../team_selector_list/index.tsx | 9 ++- app/screens/index.tsx | 12 ++-- assets/base/i18n/en.json | 2 +- 9 files changed, 109 insertions(+), 75 deletions(-) diff --git a/app/actions/remote/channel.ts b/app/actions/remote/channel.ts index e9297b915..c73fba935 100644 --- a/app/actions/remote/channel.ts +++ b/app/actions/remote/channel.ts @@ -1309,7 +1309,7 @@ export const convertGroupMessageToPrivateChannel = async (serverUrl: string, cha return {updatedChannel}; } catch (error) { - logDebug('error on convertGroupMessageToPrivateChannel', getFullErrorMessage(error)); + logError('error on convertGroupMessageToPrivateChannel', getFullErrorMessage(error)); forceLogoutIfNecessary(serverUrl, error); return {error}; } finally { diff --git a/app/components/button/index.tsx b/app/components/button/index.tsx index 9aa6c60d4..eb8bde1c3 100644 --- a/app/components/button/index.tsx +++ b/app/components/button/index.tsx @@ -1,7 +1,7 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import React, {useMemo} from 'react'; +import React, {useMemo, type ReactNode} from 'react'; import {type StyleProp, StyleSheet, Text, type TextStyle, View, type ViewStyle} from 'react-native'; import RNButton from 'react-native-button'; @@ -21,6 +21,7 @@ type Props = ConditionalProps & { testID?: string; onPress: () => void; text: string; + iconComponent?: ReactNode; } const styles = StyleSheet.create({ @@ -41,6 +42,7 @@ const Button = ({ testID, iconName, iconSize, + iconComponent, }: Props) => { const bgStyle = useMemo(() => [ buttonBackgroundStyle(theme, size, emphasis, buttonType, buttonState), @@ -61,6 +63,21 @@ const Button = ({ [iconSize], ); + let icon: ReactNode; + + if (iconComponent) { + icon = iconComponent; + } else if (iconName) { + icon = ( + + ); + } + return ( - {Boolean(iconName) && - - } + {icon} { const {formatMessage} = useIntl(); - const goToConvertToPrivateChannel = preventDoubleTap(async () => { + const goToConvertToPrivateChannel = useCallback(preventDoubleTap(async () => { await dismissBottomSheet(); const title = formatMessage({id: 'channel_info.convert_gm_to_channel.screen_title', defaultMessage: 'Convert to Private Channel'}); goToScreen(Screens.CONVERT_GM_TO_CHANNEL, title, {channelId}); - }); + }), [channelId]); return ( { + // Gotta make sure we use profiles that are in members. + // See comment in fetchChannelMemberships for more details. + + const usersById: {[id: string]: UserProfile} = {}; + users.forEach((profile) => { + if (profile.id !== currentUserId) { + usersById[profile.id] = profile; + } + }); + + const filteredUsers: UserProfile[] = []; + members.forEach((member) => { + if (usersById[member.user_id]) { + filteredUsers.push(usersById[member.user_id]); + } + }); + + return filteredUsers; +}; + const getStyleFromTheme = makeStyleSheetFromTheme((theme: Theme) => { return { loadingContainer: { @@ -74,48 +95,29 @@ const ConvertGMToChannel = ({ }; }, []); - const matchUserProfiles = (users: UserProfile[], members: ChannelMembership[]) => { - // Gotta make sure we use profiles that are in members. - // See comment in fetchChannelMemberships for more details. - - const usersById: {[id: string]: UserProfile} = {}; - users.forEach((profile) => { - if (profile.id !== currentUserId) { - usersById[profile.id] = profile; - } - }); - - const filteredUsers: UserProfile[] = []; - members.forEach((member) => { - if (usersById[member.user_id]) { - filteredUsers.push(usersById[member.user_id]); - } - }); - - return filteredUsers; - }; - useEffect(() => { mounted.current = true; - const options: GetUsersOptions = {sort: 'admin', active: true, per_page: PER_PAGE_DEFAULT}; - fetchChannelMemberships(serverUrl, channelId, options, true).then(({users, members}) => { - if (!mounted.current) { - return; - } - - if (users.length) { - setProfiles(matchUserProfiles(users, members)); - } - - setChannelMembersFetched(true); - }); - return () => { mounted.current = false; }; }, []); + useEffect(() => { + const options: GetUsersOptions = {sort: 'admin', active: true, per_page: PER_PAGE_DEFAULT}; + fetchChannelMemberships(serverUrl, channelId, options, true).then(({users, members}) => { + if (!mounted.current || !currentUserId) { + return; + } + + if (users.length) { + setProfiles(matchUserProfiles(users, members, currentUserId)); + } + + setChannelMembersFetched(true); + }); + }, [serverUrl, channelId, currentUserId]); + const showLoader = !loadingAnimationTimeout || !commonTeamsFetched || !channelMembersFetched; if (showLoader) { diff --git a/app/screens/convert_gm_to_channel/convert_gm_to_channel_form/convert_gm_to_channel_form.tsx b/app/screens/convert_gm_to_channel/convert_gm_to_channel_form/convert_gm_to_channel_form.tsx index ec1b1839a..44beb20b7 100644 --- a/app/screens/convert_gm_to_channel/convert_gm_to_channel_form/convert_gm_to_channel_form.tsx +++ b/app/screens/convert_gm_to_channel/convert_gm_to_channel_form/convert_gm_to_channel_form.tsx @@ -6,12 +6,14 @@ import {useIntl} from 'react-intl'; import {Text, View} from 'react-native'; import {convertGroupMessageToPrivateChannel, switchToChannelById} from '@actions/remote/channel'; -import {isErrorWithMessage} from '@app/utils/errors'; +import Loading from '@app/components/loading'; +import {logError} from '@app/utils/log'; import Button from '@components/button'; import {useServerUrl} from '@context/server'; import {useTheme} from '@context/theme'; +import {isErrorWithMessage} from '@utils/errors'; import {preventDoubleTap} from '@utils/tap'; -import {makeStyleSheetFromTheme} from '@utils/theme'; +import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; import {displayUsername} from '@utils/user'; import {ChannelNameInput} from '../channel_name_input'; @@ -32,6 +34,11 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme: Theme) => { errorMessage: { color: theme.dndIndicator, }, + loadingContainerStyle: { + marginRight: 10, + padding: 0, + top: -2, + }, }; }); @@ -58,10 +65,11 @@ export const ConvertGMToChannelForm = ({ const [selectedTeam, setSelectedTeam] = useState(); const [newChannelName, setNewChannelName] = useState(''); const [errorMessage, setErrorMessage] = useState(''); + const [conversionInProgress, setConversionInProgress] = useState(false); // LOL revert this default value back to false const {formatMessage} = useIntl(); const userDisplayNames = useMemo(() => profiles.map((profile) => displayUsername(profile, locale, teammateNameDisplay)), [profiles]); - const submitButtonEnabled = selectedTeam && newChannelName.trim(); + const submitButtonEnabled = !conversionInProgress && selectedTeam && newChannelName.trim(); useEffect(() => { if (commonTeams.length > 0) { @@ -74,6 +82,8 @@ export const ConvertGMToChannelForm = ({ return; } + setConversionInProgress(true); + const {updatedChannel, error} = await convertGroupMessageToPrivateChannel(serverUrl, channelId, selectedTeam.id, newChannelName); if (error) { if (isErrorWithMessage(error)) { @@ -82,16 +92,20 @@ export const ConvertGMToChannelForm = ({ setErrorMessage(formatMessage({id: 'channel_info.convert_gm_to_channel.conversion_error', defaultMessage: 'Something went wrong. Failed to convert Group Message to Private Channel.'})); } + setConversionInProgress(false); return; } if (!updatedChannel) { + logError('No updated channel received from server when converting GM to private channel'); setErrorMessage(formatMessage({id: 'channel_info.convert_gm_to_channel.conversion_error', defaultMessage: 'Something went wrong. Failed to convert Group Message to Private Channel.'})); + setConversionInProgress(false); return; } setErrorMessage(''); switchToChannelById(serverUrl, updatedChannel.id, selectedTeam.id); + setConversionInProgress(false); }), [selectedTeam, newChannelName, submitButtonEnabled]); if (commonTeams.length === 0) { @@ -105,11 +119,17 @@ export const ConvertGMToChannelForm = ({ defaultMessage: 'Conversation history will be visible to any channel members', }); - const confirmButtonText = formatMessage({ + const textConvert = formatMessage({ id: 'channel_info.convert_gm_to_channel.button_text', defaultMessage: 'Convert to Private Channel', }); + const textConverting = formatMessage({ + id: 'channel_info.convert_gm_to_channel.button_text_converting', + defaultMessage: 'Converting...', + }); + + const confirmButtonText = conversionInProgress ? textConverting : textConvert; const defaultUserDisplayNames = intl.formatMessage({id: 'channel_info.convert_gm_to_channel.warning.body.yourself', defaultMessage: 'yourself'}); const memberNames = profiles.length > 0 ? intl.formatList(userDisplayNames) : defaultUserDisplayNames; const messageBoxBody = intl.formatMessage({ @@ -119,6 +139,13 @@ export const ConvertGMToChannelForm = ({ memberNames, }); + const buttonIcon = conversionInProgress ? ( + + ) : null; + return ( { errorMessage && diff --git a/app/screens/convert_gm_to_channel/team_selector.tsx b/app/screens/convert_gm_to_channel/team_selector.tsx index 425415bbf..a66f1b443 100644 --- a/app/screens/convert_gm_to_channel/team_selector.tsx +++ b/app/screens/convert_gm_to_channel/team_selector.tsx @@ -1,7 +1,7 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import React, {useCallback, useEffect, useState} from 'react'; +import React, {useCallback, useMemo} from 'react'; import {useIntl} from 'react-intl'; import {Platform} from 'react-native'; @@ -19,6 +19,9 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme: Theme) => { borderBottomWidth: 1, borderColor: changeOpacity(theme.centerChannelColor, 0.08), }, + labelContainerStyle: { + flexShrink: 0, + }, }; }); @@ -33,15 +36,14 @@ export const TeamSelector = ({commonTeams, onSelectTeam, selectedTeamId}: Props) const theme = useTheme(); const styles = getStyleFromTheme(theme); - const [selectedTeam, setSelectedTeam] = useState(); - const label = formatMessage({id: 'channel_into.convert_gm_to_channel.team_selector.label', defaultMessage: 'Team'}); const placeholder = formatMessage({id: 'channel_into.convert_gm_to_channel.team_selector.placeholder', defaultMessage: 'Select a Team'}); + const selectedTeam = useMemo(() => commonTeams.find((t) => t.id === selectedTeamId), [commonTeams, selectedTeamId]); + const selectTeam = useCallback((teamId: string) => { const team = commonTeams.find((t) => t.id === teamId); if (team) { - setSelectedTeam(team); onSelectTeam(team); } }, []); @@ -52,13 +54,6 @@ export const TeamSelector = ({commonTeams, onSelectTeam, selectedTeamId}: Props) goToScreen(Screens.TEAM_SELECTOR_LIST, title, {teams: commonTeams, selectTeam, selectedTeamId}); }), [commonTeams, selectTeam, selectedTeamId]); - useEffect(() => { - if (selectedTeamId && !selectedTeam) { - const team = commonTeams.find((t) => t.id === selectedTeamId); - setSelectedTeam(team); - } - }, [selectedTeamId]); - return ( ); }; diff --git a/app/screens/convert_gm_to_channel/team_selector_list/index.tsx b/app/screens/convert_gm_to_channel/team_selector_list/index.tsx index d406f90b1..65dfd7bb4 100644 --- a/app/screens/convert_gm_to_channel/team_selector_list/index.tsx +++ b/app/screens/convert_gm_to_channel/team_selector_list/index.tsx @@ -1,6 +1,7 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. +import {debounce} from 'lodash'; import React, {useCallback, useMemo, useState} from 'react'; import {StyleSheet, View} from 'react-native'; @@ -27,18 +28,16 @@ type Props = { const TeamSelectorList = ({teams, selectTeam}: Props) => { const theme = useTheme(); - + const [filteredTeams, setFilteredTeam] = useState(teams); const color = useMemo(() => changeOpacity(theme.centerChannelColor, 0.72), [theme]); - const [filteredTeams, setFilteredTeam] = useState(teams); - - const handleOnChangeSearchText = useCallback((searchTerm: string) => { + const handleOnChangeSearchText = useCallback(debounce((searchTerm: string) => { if (searchTerm === '') { setFilteredTeam(teams); } else { setFilteredTeam(teams.filter((team) => team.display_name.includes(searchTerm) || team.name.includes(searchTerm))); } - }, [teams]); + }, 200), [teams]); const handleOnPress = useCallback(preventDoubleTap((teamId: string) => { selectTeam(teamId); diff --git a/app/screens/index.tsx b/app/screens/index.tsx index c820b098e..5707b3b4f 100644 --- a/app/screens/index.tsx +++ b/app/screens/index.tsx @@ -90,6 +90,9 @@ Navigation.setLazyComponentRegistrator((screenName) => { case Screens.CODE: screen = withServerDatabase(require('@screens/code').default); break; + case Screens.CONVERT_GM_TO_CHANNEL: + screen = withServerDatabase(require('@screens/convert_gm_to_channel').default); + break; case Screens.CREATE_OR_EDIT_CHANNEL: screen = withServerDatabase(require('@screens/create_or_edit_channel').default); break; @@ -242,6 +245,9 @@ Navigation.setLazyComponentRegistrator((screenName) => { case Screens.TABLE: screen = withServerDatabase(require('@screens/table').default); break; + case Screens.TEAM_SELECTOR_LIST: + screen = withServerDatabase(require('@screens/convert_gm_to_channel/team_selector_list').default); + break; case Screens.TERMS_OF_SERVICE: screen = withServerDatabase(require('@screens/terms_of_service').default); break; @@ -262,12 +268,6 @@ Navigation.setLazyComponentRegistrator((screenName) => { case Screens.CALL: screen = withServerDatabase(require('@calls/screens/call_screen').default); break; - case Screens.CONVERT_GM_TO_CHANNEL: - screen = withServerDatabase(require('@screens/convert_gm_to_channel').default); - break; - case Screens.TEAM_SELECTOR_LIST: - screen = withServerDatabase(require('@screens/convert_gm_to_channel/team_selector_list').default); - break; } if (screen) { diff --git a/assets/base/i18n/en.json b/assets/base/i18n/en.json index f7c88f508..b2e4c053e 100644 --- a/assets/base/i18n/en.json +++ b/assets/base/i18n/en.json @@ -1,5 +1,4 @@ { - "\"channel_modal.name\": \"Name\",": "", "about.date": "Build Date:", "about.enterpriseEditione1": "Enterprise Edition", "about.enterpriseEditionLearn": "Learn more about Enterprise Edition at ", @@ -122,6 +121,7 @@ "channel_info.convert_failed": "We were unable to convert {displayName} to a private channel.", "channel_info.convert_gm_to_channel": "Convert to a Private Channel", "channel_info.convert_gm_to_channel.button_text": "Convert to Private Channel", + "channel_info.convert_gm_to_channel.button_text_converting": "Converting...", "channel_info.convert_gm_to_channel.conversion_error": "Something went wrong. Failed to convert Group Message to Private Channel.", "channel_info.convert_gm_to_channel.loading.footer": "Fetching details...", "channel_info.convert_gm_to_channel.screen_title": "Convert to Private Channel",