Review fixes
This commit is contained in:
parent
8fe529a4f5
commit
eebd48466f
8 changed files with 139 additions and 118 deletions
|
|
@ -10,6 +10,8 @@ import {useServerUrl} from '@context/server';
|
|||
import ConvertGMToChannelForm from './convert_gm_to_channel_form';
|
||||
import {Loader} from './loader';
|
||||
|
||||
import type UserProfile from '../user_profile/user_profile';
|
||||
|
||||
type Props = {
|
||||
channelId: string;
|
||||
currentUserId?: string;
|
||||
|
|
@ -30,21 +32,49 @@ const ConvertGMToChannel = ({
|
|||
const serverUrl = useServerUrl();
|
||||
const mounted = useRef(false);
|
||||
|
||||
const loadingAnimationTimeoutRef = useRef<NodeJS.Timeout>();
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(() => setLoadingAnimationTimeout(true), loadingIndicatorTimeout);
|
||||
loadingAnimationTimeoutRef.current = setTimeout(() => setLoadingAnimationTimeout(true), loadingIndicatorTimeout);
|
||||
async function work() {
|
||||
const {teams} = await getGroupMessageMembersCommonTeams(serverUrl, channelId);
|
||||
if (!teams) {
|
||||
if (!teams || !mounted.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
setCommonTeams(teams);
|
||||
setCommonTeamsFetched(true);
|
||||
}
|
||||
|
||||
work();
|
||||
|
||||
return () => {
|
||||
if (loadingAnimationTimeoutRef.current) {
|
||||
clearTimeout(loadingAnimationTimeoutRef.current);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
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;
|
||||
|
||||
|
|
@ -55,26 +85,7 @@ const ConvertGMToChannel = ({
|
|||
}
|
||||
|
||||
if (users.length) {
|
||||
// Gotta make sure we use profiles that are in members.
|
||||
// See comment in fetchChannelMemberships for more details.
|
||||
|
||||
const usersById: {[id: string]: UserProfile} = {};
|
||||
// eslint-disable-next-line max-nested-callbacks
|
||||
users.forEach((profile) => {
|
||||
if (profile.id !== currentUserId) {
|
||||
usersById[profile.id] = profile;
|
||||
}
|
||||
});
|
||||
|
||||
// eslint-disable-next-line max-nested-callbacks
|
||||
const filteredUsers: UserProfile[] = [];
|
||||
// eslint-disable-next-line max-nested-callbacks
|
||||
members.forEach((member) => {
|
||||
if (usersById[member.user_id]) {
|
||||
filteredUsers.push(usersById[member.user_id]);
|
||||
}
|
||||
});
|
||||
setProfiles(filteredUsers);
|
||||
setProfiles(matchUserProfiles(users, members));
|
||||
}
|
||||
|
||||
setChannelMembersFetched(true);
|
||||
|
|
|
|||
|
|
@ -3,15 +3,16 @@
|
|||
|
||||
import React, {useCallback, useEffect, useState} from 'react';
|
||||
import {useIntl} from 'react-intl';
|
||||
import {View} from 'react-native';
|
||||
import {Text, View} from 'react-native';
|
||||
|
||||
import {convertGroupMessageToPrivateChannel, switchToChannelById} from '@actions/remote/channel';
|
||||
import {isErrorWithMessage} from '@app/utils/errors';
|
||||
import {preventDoubleTap} from '@app/utils/tap';
|
||||
import Button from '@components/button';
|
||||
import {useServerUrl} from '@context/server';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {logError} from '@utils/log';
|
||||
import {makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {displayUsername} from '@utils/user';
|
||||
import Button from '@components/button';
|
||||
|
||||
import {ChannelNameInput} from '../channel_name_input';
|
||||
import MessageBox from '../message_box/message_box';
|
||||
|
|
@ -19,7 +20,7 @@ import {TeamSelector} from '../team_selector';
|
|||
|
||||
import {NoCommonTeamForm} from './no_common_teams_form';
|
||||
|
||||
const getStyleFromTheme = makeStyleSheetFromTheme(() => {
|
||||
const getStyleFromTheme = makeStyleSheetFromTheme((theme: Theme) => {
|
||||
return {
|
||||
container: {
|
||||
paddingVertical: 24,
|
||||
|
|
@ -28,6 +29,9 @@ const getStyleFromTheme = makeStyleSheetFromTheme(() => {
|
|||
flexDirection: 'column',
|
||||
gap: 24,
|
||||
},
|
||||
errorMessage: {
|
||||
color: theme.dndIndicator,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
|
|
@ -53,6 +57,7 @@ export const ConvertGMToChannelForm = ({
|
|||
|
||||
const [selectedTeam, setSelectedTeam] = useState<Team>();
|
||||
const [newChannelName, setNewChannelName] = useState<string>('');
|
||||
const [errorMessage, setErrorMessage] = useState<string>('Something went wrong. Failed to convert Group Message to Private Channel');
|
||||
|
||||
const submitButtonEnabled = selectedTeam && newChannelName.trim();
|
||||
|
||||
|
|
@ -66,26 +71,32 @@ export const ConvertGMToChannelForm = ({
|
|||
if (commonTeams.length > 0) {
|
||||
setSelectedTeam(commonTeams[0]);
|
||||
}
|
||||
}, [commonTeams]);
|
||||
}, []);
|
||||
|
||||
const handleOnPress = useCallback(async () => {
|
||||
const handleOnPress = useCallback(preventDoubleTap(async () => {
|
||||
if (!submitButtonEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {updatedChannel, error} = await convertGroupMessageToPrivateChannel(serverUrl, channelId, selectedTeam.id, newChannelName);
|
||||
|
||||
if (error) {
|
||||
logError(error);
|
||||
if (isErrorWithMessage(error)) {
|
||||
setErrorMessage(error.message);
|
||||
} else {
|
||||
setErrorMessage(formatMessage({id: 'channel_info.convert_gm_to_channel.conversion_error', defaultMessage: 'Something went wrong. Failed to convert Group Message to Private Channel.'}));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!updatedChannel) {
|
||||
setErrorMessage(formatMessage({id: 'channel_info.convert_gm_to_channel.conversion_error', defaultMessage: 'Something went wrong. Failed to convert Group Message to Private Channel.'}));
|
||||
return;
|
||||
}
|
||||
|
||||
await switchToChannelById(serverUrl, updatedChannel.id, selectedTeam.id);
|
||||
}, [selectedTeam, newChannelName, submitButtonEnabled]);
|
||||
setErrorMessage('');
|
||||
switchToChannelById(serverUrl, updatedChannel.id, selectedTeam.id);
|
||||
}), [selectedTeam, newChannelName, submitButtonEnabled]);
|
||||
|
||||
const messageBoxHeader = intl.formatMessage({
|
||||
id: 'channel_info.convert_gm_to_channel.warning.header',
|
||||
|
|
@ -130,6 +141,12 @@ export const ConvertGMToChannelForm = ({
|
|||
buttonType={submitButtonEnabled ? 'destructive' : 'disabled'}
|
||||
size='lg'
|
||||
/>
|
||||
{
|
||||
errorMessage &&
|
||||
<Text style={styles.errorMessage}>
|
||||
{errorMessage}
|
||||
</Text>
|
||||
}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -5,11 +5,12 @@ import React, {useCallback} from 'react';
|
|||
import {useIntl} from 'react-intl';
|
||||
import {View, type ViewStyle} from 'react-native';
|
||||
|
||||
import {preventDoubleTap} from '@app/utils/tap';
|
||||
import Button from '@components/button';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {popTopScreen} from '@screens/navigation';
|
||||
|
||||
import MessageBox from '../message_box';
|
||||
import MessageBox from '../message_box/message_box';
|
||||
|
||||
type Props = {
|
||||
containerStyles: ViewStyle;
|
||||
|
|
@ -31,9 +32,9 @@ export const NoCommonTeamForm = ({
|
|||
defaultMessage: 'Group Message cannot be converted to a channel because members are not a part of the same team. Add all members to a single team to convert this group message to a channel.',
|
||||
});
|
||||
|
||||
const handleOnPress = useCallback(() => {
|
||||
const handleOnPress = useCallback(preventDoubleTap(() => {
|
||||
popTopScreen();
|
||||
}, []);
|
||||
}), []);
|
||||
|
||||
const buttonText = formatMessage({
|
||||
id: 'generic.back',
|
||||
|
|
|
|||
|
|
@ -85,8 +85,6 @@ const getDangerStylesFromTheme = makeStyleSheetFromTheme((theme: Theme) => {
|
|||
});
|
||||
|
||||
const getStyleFromTheme = (theme: Theme, kind: MessageBoxTypes | undefined) => {
|
||||
const baseStyles = getBaseStyles(theme);
|
||||
|
||||
let kindStyles;
|
||||
switch (kind) {
|
||||
case 'danger': {
|
||||
|
|
@ -100,7 +98,6 @@ const getStyleFromTheme = (theme: Theme, kind: MessageBoxTypes | undefined) => {
|
|||
}
|
||||
|
||||
return {
|
||||
...baseStyles,
|
||||
...kindStyles,
|
||||
};
|
||||
};
|
||||
|
|
@ -111,25 +108,25 @@ const MessageBox = ({
|
|||
type,
|
||||
}: Props) => {
|
||||
const theme = useTheme();
|
||||
const styles = getBaseStyles(theme);
|
||||
const foo = getStyleFromTheme(theme, type);
|
||||
const baseStyle = getBaseStyles(theme);
|
||||
const kindStyle = getStyleFromTheme(theme, type);
|
||||
|
||||
return (
|
||||
<View style={[styles.container, foo.container]}>
|
||||
<View style={styles.iconContainer}>
|
||||
<View style={[baseStyle.container, kindStyle.container]}>
|
||||
<View style={baseStyle.iconContainer}>
|
||||
<CompassIcon
|
||||
name='exclamation-thick'
|
||||
style={[styles.icon, foo.icon]}
|
||||
style={[baseStyle.icon, kindStyle.icon]}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.textContainer}>
|
||||
<View style={baseStyle.textContainer}>
|
||||
<View>
|
||||
<Text style={[styles.heading, foo.heading]}>
|
||||
<Text style={baseStyle.heading}>
|
||||
{header}
|
||||
</Text>
|
||||
</View>
|
||||
<View>
|
||||
<Text style={[styles.body, foo.body]}>
|
||||
<Text style={baseStyle.body}>
|
||||
{body}
|
||||
</Text>
|
||||
</View>
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ export const TeamSelector = ({commonTeams, onSelectTeam, selectedTeamId}: Props)
|
|||
const placeholder = formatMessage({id: 'channel_into.convert_gm_to_channel.team_selector.placeholder', defaultMessage: 'Select a Team'});
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedTeamId) {
|
||||
if (selectedTeamId && !selectedTeam) {
|
||||
const team = commonTeams.find((t) => t.id === selectedTeamId);
|
||||
setSelectedTeam(team);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,69 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import TeamSelectorList from './team_selector_list';
|
||||
import React, {useCallback, useMemo, useState} from 'react';
|
||||
import {StyleSheet, View} from 'react-native';
|
||||
|
||||
import {preventDoubleTap} from '@app/utils/tap';
|
||||
import SearchBar from '@components/search';
|
||||
import TeamList from '@components/team_list';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {popTopScreen} from '@screens/navigation';
|
||||
import {changeOpacity, getKeyboardAppearanceFromTheme} from '@utils/theme';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
padding: 12,
|
||||
},
|
||||
listContainer: {
|
||||
marginTop: 12,
|
||||
},
|
||||
});
|
||||
|
||||
type Props = {
|
||||
teams: Team[];
|
||||
selectTeam: (teamId: string) => void;
|
||||
}
|
||||
|
||||
const TeamSelectorList = ({teams, selectTeam}: Props) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const color = useMemo(() => changeOpacity(theme.centerChannelColor, 0.72), [theme]);
|
||||
|
||||
const [filteredTeams, setFilteredTeam] = useState(teams);
|
||||
|
||||
const handleOnChangeSearchText = useCallback((searchTerm: string) => {
|
||||
if (searchTerm === '') {
|
||||
setFilteredTeam(teams);
|
||||
} else {
|
||||
setFilteredTeam(teams.filter((team) => team.display_name.includes(searchTerm) || team.name.includes(searchTerm)));
|
||||
}
|
||||
}, [teams]);
|
||||
|
||||
const handleOnPress = useCallback(preventDoubleTap((teamId: string) => {
|
||||
selectTeam(teamId);
|
||||
popTopScreen();
|
||||
}), []);
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<SearchBar
|
||||
autoCapitalize='none'
|
||||
autoFocus={true}
|
||||
keyboardAppearance={getKeyboardAppearanceFromTheme(theme)}
|
||||
placeholderTextColor={color}
|
||||
searchIconColor={color}
|
||||
testID='convert_gm_to_channel_team_search_bar'
|
||||
onChangeText={handleOnChangeSearchText}
|
||||
/>
|
||||
<View style={styles.listContainer}>
|
||||
<TeamList
|
||||
teams={filteredTeams}
|
||||
onPress={handleOnPress}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default TeamSelectorList;
|
||||
|
|
|
|||
|
|
@ -1,69 +0,0 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {useCallback, useMemo, useState} from 'react';
|
||||
import {View} from 'react-native';
|
||||
|
||||
import SearchBar from '@components/search';
|
||||
import TeamList from '@components/team_list';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {popTopScreen} from '@screens/navigation';
|
||||
import {changeOpacity, getKeyboardAppearanceFromTheme, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
|
||||
const style = StyleSheet.create({
|
||||
container: {
|
||||
padding: 12,
|
||||
},
|
||||
listContainer: {
|
||||
marginTop: 12,
|
||||
},
|
||||
});
|
||||
|
||||
type Props = {
|
||||
teams: Team[];
|
||||
selectTeam: (teamId: string) => void;
|
||||
}
|
||||
|
||||
const TeamSelectorList = ({teams, selectTeam}: Props) => {
|
||||
const theme = useTheme();
|
||||
const styles = getStyleSheet(theme);
|
||||
|
||||
const color = useMemo(() => changeOpacity(theme.centerChannelColor, 0.72), [theme]);
|
||||
|
||||
const [filteredTeams, setFilteredTeam] = useState(teams);
|
||||
|
||||
const handleOnChangeSearchText = useCallback((searchTerm: string) => {
|
||||
if (searchTerm === '') {
|
||||
setFilteredTeam(teams);
|
||||
} else {
|
||||
setFilteredTeam(teams.filter((team) => team.display_name.includes(searchTerm) || team.name.includes(searchTerm)));
|
||||
}
|
||||
}, [teams]);
|
||||
|
||||
const handleOnPress = useCallback((teamId: string) => {
|
||||
selectTeam(teamId);
|
||||
popTopScreen();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<SearchBar
|
||||
autoCapitalize='none'
|
||||
autoFocus={true}
|
||||
keyboardAppearance={getKeyboardAppearanceFromTheme(theme)}
|
||||
placeholderTextColor={color}
|
||||
searchIconColor={color}
|
||||
testID='convert_gm_to_channel_team_search_bar'
|
||||
onChangeText={handleOnChangeSearchText}
|
||||
/>
|
||||
<View style={styles.listContainer}>
|
||||
<TeamList
|
||||
teams={filteredTeams}
|
||||
onPress={handleOnPress}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default TeamSelectorList;
|
||||
|
|
@ -1099,6 +1099,7 @@
|
|||
"channel_into.convert_gm_to_channel.team_selector.label": "Team",
|
||||
"channel_into.convert_gm_to_channel.team_selector.placeholder": "Select a Team",
|
||||
"channel_info.convert_gm_to_channel.button_text": "Convert to Private Channel",
|
||||
"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.team_selector_list.title": "Select Team",
|
||||
"generic.back": "Back"
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue