[IMPROVE] Migrate UIKit/MultiSelect to ActionSheet (#4255)
* Migrate UIKit/MultiSelect to ActionSheet * Fix no options initially on CreateDiscussion view * Change backgroundColor and use colors from useTheme * Define missing types * onSearch function for the ActionSheet * Add onClose function to the ActionSheet and use colors from useTheme * fix theme and bottomSheet * fix actionSheet * fix style Co-authored-by: GleidsonDaniel <gleidson10daniel@hotmail.com>
This commit is contained in:
parent
785ae0325b
commit
dd48402214
|
@ -101,6 +101,11 @@ const ActionSheet = React.memo(
|
|||
</>
|
||||
);
|
||||
|
||||
const onClose = () => {
|
||||
toggleVisible();
|
||||
data?.onClose && data?.onClose();
|
||||
};
|
||||
|
||||
const renderBackdrop = useCallback(
|
||||
props => (
|
||||
<BottomSheetBackdrop
|
||||
|
@ -134,7 +139,7 @@ const ActionSheet = React.memo(
|
|||
enablePanDownToClose
|
||||
style={{ ...styles.container, ...bottomSheet }}
|
||||
backgroundStyle={{ backgroundColor: colors.focusedBackground }}
|
||||
onChange={index => index === -1 && toggleVisible()}
|
||||
onChange={index => index === -1 && onClose()}
|
||||
{...androidTablet}>
|
||||
<BottomSheetContent options={data?.options} hide={hide} children={data?.children} hasCancel={data?.hasCancel} />
|
||||
</BottomSheet>
|
||||
|
|
|
@ -53,7 +53,7 @@ const BottomSheetContent = React.memo(({ options, hasCancel, hide, children }: I
|
|||
/>
|
||||
);
|
||||
}
|
||||
return <BottomSheetView>{children}</BottomSheetView>;
|
||||
return <BottomSheetView style={styles.contentContainer}>{children}</BottomSheetView>;
|
||||
});
|
||||
|
||||
export default BottomSheetContent;
|
||||
|
|
|
@ -8,7 +8,7 @@ import { useTheme } from '../../theme';
|
|||
export const Handle = React.memo(() => {
|
||||
const { theme } = useTheme();
|
||||
return (
|
||||
<View style={[styles.handle, { backgroundColor: themes[theme].focusedBackground }]} testID='action-sheet-handle'>
|
||||
<View style={[styles.handle]} testID='action-sheet-handle'>
|
||||
<View style={[styles.handleIndicator, { backgroundColor: themes[theme].auxiliaryText }]} />
|
||||
</View>
|
||||
);
|
||||
|
|
|
@ -20,7 +20,8 @@ export type TActionSheetOptions = {
|
|||
hasCancel?: boolean;
|
||||
type?: string;
|
||||
children?: React.ReactElement | null;
|
||||
snaps?: string[] | number[];
|
||||
snaps?: (string | number)[];
|
||||
onClose?: () => void;
|
||||
};
|
||||
export interface IActionSheetProvider {
|
||||
showActionSheet: (item: TActionSheetOptions) => void;
|
||||
|
|
|
@ -63,5 +63,15 @@ export default StyleSheet.create({
|
|||
},
|
||||
rightContainer: {
|
||||
paddingLeft: 12
|
||||
},
|
||||
footerButtonsContainer: {
|
||||
flexDirection: 'row',
|
||||
paddingTop: 16
|
||||
},
|
||||
buttonSeparator: {
|
||||
marginRight: 8
|
||||
},
|
||||
contentContainer: {
|
||||
flex: 1
|
||||
}
|
||||
});
|
||||
|
|
|
@ -3,50 +3,50 @@ import { Text, View } from 'react-native';
|
|||
import Touchable from 'react-native-platform-touchable';
|
||||
import FastImage from 'react-native-fast-image';
|
||||
|
||||
import { themes } from '../../../lib/constants';
|
||||
import { textParser } from '../utils';
|
||||
import { CustomIcon } from '../../CustomIcon';
|
||||
import styles from './styles';
|
||||
import { IItemData } from '.';
|
||||
import { TSupportedThemes } from '../../../theme';
|
||||
import { useTheme } from '../../../theme';
|
||||
|
||||
interface IChip {
|
||||
item: IItemData;
|
||||
onSelect: (item: IItemData) => void;
|
||||
style?: object;
|
||||
theme: TSupportedThemes;
|
||||
}
|
||||
|
||||
interface IChips {
|
||||
items: IItemData[];
|
||||
onSelect: (item: IItemData) => void;
|
||||
style?: object;
|
||||
theme: TSupportedThemes;
|
||||
}
|
||||
|
||||
const keyExtractor = (item: IItemData) => item.value.toString();
|
||||
|
||||
const Chip = ({ item, onSelect, style, theme }: IChip) => (
|
||||
const Chip = ({ item, onSelect, style }: IChip) => {
|
||||
const { colors } = useTheme();
|
||||
return (
|
||||
<Touchable
|
||||
key={item.value}
|
||||
onPress={() => onSelect(item)}
|
||||
style={[styles.chip, { backgroundColor: themes[theme].auxiliaryBackground }, style]}
|
||||
background={Touchable.Ripple(themes[theme].bannerBackground)}>
|
||||
style={[styles.chip, { backgroundColor: colors.auxiliaryBackground }, style]}
|
||||
background={Touchable.Ripple(colors.bannerBackground)}>
|
||||
<>
|
||||
{item.imageUrl ? <FastImage style={styles.chipImage} source={{ uri: item.imageUrl }} /> : null}
|
||||
<Text numberOfLines={1} style={[styles.chipText, { color: themes[theme].titleText }]}>
|
||||
<Text numberOfLines={1} style={[styles.chipText, { color: colors.titleText }]}>
|
||||
{textParser([item.text])}
|
||||
</Text>
|
||||
<CustomIcon name='close' size={16} color={themes[theme].auxiliaryText} />
|
||||
<CustomIcon name='close' size={16} color={colors.auxiliaryText} />
|
||||
</>
|
||||
</Touchable>
|
||||
);
|
||||
);
|
||||
};
|
||||
Chip.propTypes = {};
|
||||
|
||||
const Chips = ({ items, onSelect, style, theme }: IChips) => (
|
||||
const Chips = ({ items, onSelect, style }: IChips) => (
|
||||
<View style={styles.chips}>
|
||||
{items.map(item => (
|
||||
<Chip key={keyExtractor(item)} item={item} onSelect={onSelect} style={style} theme={theme} />
|
||||
<Chip key={keyExtractor(item)} item={item} onSelect={onSelect} style={style} />
|
||||
))}
|
||||
</View>
|
||||
);
|
||||
|
|
|
@ -3,15 +3,13 @@ import { Text, View } from 'react-native';
|
|||
import Touchable from 'react-native-platform-touchable';
|
||||
|
||||
import { CustomIcon } from '../../CustomIcon';
|
||||
import { themes } from '../../../lib/constants';
|
||||
import ActivityIndicator from '../../ActivityIndicator';
|
||||
import styles from './styles';
|
||||
import { TSupportedThemes } from '../../../theme';
|
||||
import { useTheme } from '../../../theme';
|
||||
|
||||
interface IInput {
|
||||
children?: JSX.Element;
|
||||
onPress: () => void;
|
||||
theme: TSupportedThemes;
|
||||
inputStyle?: object;
|
||||
disabled?: boolean | null;
|
||||
placeholder?: string;
|
||||
|
@ -19,21 +17,23 @@ interface IInput {
|
|||
innerInputStyle?: object;
|
||||
}
|
||||
|
||||
const Input = ({ children, onPress, theme, loading, inputStyle, placeholder, disabled, innerInputStyle }: IInput) => (
|
||||
const Input = ({ children, onPress, loading, inputStyle, placeholder, disabled, innerInputStyle }: IInput) => {
|
||||
const { colors } = useTheme();
|
||||
return (
|
||||
<Touchable
|
||||
onPress={onPress}
|
||||
style={[{ backgroundColor: themes[theme].backgroundColor }, inputStyle]}
|
||||
background={Touchable.Ripple(themes[theme].bannerBackground)}
|
||||
style={[{ backgroundColor: colors.backgroundColor }, inputStyle]}
|
||||
background={Touchable.Ripple(colors.bannerBackground)}
|
||||
disabled={disabled}>
|
||||
<View style={[styles.input, { borderColor: themes[theme].separatorColor }, innerInputStyle]}>
|
||||
{placeholder ? <Text style={[styles.pickerText, { color: themes[theme].auxiliaryText }]}>{placeholder}</Text> : children}
|
||||
<View style={[styles.input, { borderColor: colors.separatorColor }, innerInputStyle]}>
|
||||
{placeholder ? <Text style={[styles.pickerText, { color: colors.auxiliaryText }]}>{placeholder}</Text> : children}
|
||||
{loading ? (
|
||||
<ActivityIndicator style={[styles.loading, styles.icon]} />
|
||||
<ActivityIndicator style={styles.icon} />
|
||||
) : (
|
||||
<CustomIcon name='chevron-down' size={22} color={themes[theme].auxiliaryText} style={styles.icon} />
|
||||
<CustomIcon name='chevron-down' size={22} color={colors.auxiliaryText} style={styles.icon} />
|
||||
)}
|
||||
</View>
|
||||
</Touchable>
|
||||
);
|
||||
|
||||
);
|
||||
};
|
||||
export default Input;
|
||||
|
|
|
@ -1,61 +1,54 @@
|
|||
import React from 'react';
|
||||
import { FlatList, Text } from 'react-native';
|
||||
import { Text } from 'react-native';
|
||||
import Touchable from 'react-native-platform-touchable';
|
||||
import FastImage from 'react-native-fast-image';
|
||||
import { FlatList } from 'react-native-gesture-handler';
|
||||
|
||||
import Check from '../../Check';
|
||||
import * as List from '../../List';
|
||||
import { textParser } from '../utils';
|
||||
import { themes } from '../../../lib/constants';
|
||||
import styles from './styles';
|
||||
import { IItemData } from '.';
|
||||
import { TSupportedThemes } from '../../../theme';
|
||||
import { useTheme } from '../../../theme';
|
||||
|
||||
interface IItem {
|
||||
item: IItemData;
|
||||
selected?: string;
|
||||
onSelect: Function;
|
||||
theme: TSupportedThemes;
|
||||
}
|
||||
|
||||
interface IItems {
|
||||
items: IItemData[];
|
||||
selected: string[];
|
||||
onSelect: Function;
|
||||
theme: TSupportedThemes;
|
||||
}
|
||||
|
||||
const keyExtractor = (item: IItemData) => item.value.toString();
|
||||
const keyExtractor = (item: IItemData) => item.value?.name || item.text?.text;
|
||||
|
||||
// RectButton doesn't work on modal (Android)
|
||||
const Item = ({ item, selected, onSelect, theme }: IItem) => {
|
||||
const Item = ({ item, selected, onSelect }: IItem) => {
|
||||
const itemName = item.value?.name || item.text.text.toLowerCase();
|
||||
const { colors } = useTheme();
|
||||
return (
|
||||
<Touchable
|
||||
testID={`multi-select-item-${itemName}`}
|
||||
key={itemName}
|
||||
onPress={() => onSelect(item)}
|
||||
style={[styles.item, { backgroundColor: themes[theme].backgroundColor }]}>
|
||||
<Touchable testID={`multi-select-item-${itemName}`} key={itemName} onPress={() => onSelect(item)} style={[styles.item]}>
|
||||
<>
|
||||
{item.imageUrl ? <FastImage style={styles.itemImage} source={{ uri: item.imageUrl }} /> : null}
|
||||
<Text style={{ color: themes[theme].titleText }}>{textParser([item.text])}</Text>
|
||||
<Text style={{ color: colors.titleText }}>{textParser([item.text])}</Text>
|
||||
{selected ? <Check /> : null}
|
||||
</>
|
||||
</Touchable>
|
||||
);
|
||||
};
|
||||
|
||||
const Items = ({ items, selected, onSelect, theme }: IItems) => (
|
||||
const Items = ({ items, selected, onSelect }: IItems) => (
|
||||
<FlatList
|
||||
data={items}
|
||||
style={[styles.items, { backgroundColor: themes[theme].backgroundColor }]}
|
||||
contentContainerStyle={[styles.itemContent, { backgroundColor: themes[theme].backgroundColor }]}
|
||||
style={[styles.items]}
|
||||
contentContainerStyle={[styles.itemContent]}
|
||||
keyboardShouldPersistTaps='always'
|
||||
ItemSeparatorComponent={List.Separator}
|
||||
keyExtractor={keyExtractor}
|
||||
renderItem={({ item }) => (
|
||||
<Item item={item} onSelect={onSelect} theme={theme} selected={selected.find(s => s === item.value)} />
|
||||
)}
|
||||
renderItem={({ item }) => <Item item={item} onSelect={onSelect} selected={selected.find(s => s === item.value)} />}
|
||||
/>
|
||||
);
|
||||
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
import React, { useState } from 'react';
|
||||
import { View } from 'react-native';
|
||||
|
||||
import { FormTextInput } from '../../TextInput/FormTextInput';
|
||||
import { textParser } from '../utils';
|
||||
import I18n from '../../../i18n';
|
||||
import Items from './Items';
|
||||
import styles from './styles';
|
||||
import { useTheme } from '../../../theme';
|
||||
import { IItemData } from '.';
|
||||
import { debounce } from '../../../lib/methods/helpers/debounce';
|
||||
import { isIOS } from '../../../lib/methods/helpers';
|
||||
import { useActionSheet } from '../../ActionSheet';
|
||||
|
||||
interface IMultiSelectContentProps {
|
||||
onSearch?: (keyword: string) => IItemData[] | Promise<IItemData[] | undefined>;
|
||||
options?: IItemData[];
|
||||
multiselect: boolean;
|
||||
select: React.Dispatch<any>;
|
||||
onChange: Function;
|
||||
setCurrentValue: React.Dispatch<React.SetStateAction<string>>;
|
||||
onHide: Function;
|
||||
selectedItems: string[];
|
||||
}
|
||||
|
||||
export const MultiSelectContent = React.memo(
|
||||
({ onSearch, options, multiselect, select, onChange, setCurrentValue, onHide, selectedItems }: IMultiSelectContentProps) => {
|
||||
const { colors } = useTheme();
|
||||
const [selected, setSelected] = useState<string[]>(Array.isArray(selectedItems) ? selectedItems : []);
|
||||
const [items, setItems] = useState<IItemData[] | undefined>(options);
|
||||
const { hideActionSheet } = useActionSheet();
|
||||
|
||||
const onSelect = (item: IItemData) => {
|
||||
const {
|
||||
value,
|
||||
text: { text }
|
||||
} = item;
|
||||
if (multiselect) {
|
||||
let newSelect = [];
|
||||
if (!selected.includes(value)) {
|
||||
newSelect = [...selected, value];
|
||||
} else {
|
||||
newSelect = selected.filter((s: any) => s !== value);
|
||||
}
|
||||
setSelected(newSelect);
|
||||
select(newSelect);
|
||||
onChange({ value: newSelect });
|
||||
} else {
|
||||
onChange({ value });
|
||||
setCurrentValue(text);
|
||||
onHide();
|
||||
}
|
||||
};
|
||||
|
||||
const handleSearch = debounce(
|
||||
async (text: string) => {
|
||||
if (onSearch) {
|
||||
const res = await onSearch(text);
|
||||
setItems(res);
|
||||
} else {
|
||||
setItems(options?.filter((option: any) => textParser([option.text]).toLowerCase().includes(text.toLowerCase())));
|
||||
}
|
||||
},
|
||||
onSearch ? 300 : 0
|
||||
);
|
||||
|
||||
return (
|
||||
<View style={[styles.actionSheetContainer]}>
|
||||
<FormTextInput
|
||||
testID='multi-select-search'
|
||||
onChangeText={handleSearch}
|
||||
placeholder={I18n.t('Search')}
|
||||
inputStyle={{ backgroundColor: colors.focusedBackground }}
|
||||
bottomSheet={isIOS}
|
||||
onSubmitEditing={() => {
|
||||
setTimeout(() => {
|
||||
hideActionSheet();
|
||||
}, 150);
|
||||
}}
|
||||
/>
|
||||
<Items items={items || []} selected={selected} onSelect={onSelect} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
);
|
|
@ -1,29 +1,15 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import {
|
||||
Animated,
|
||||
Easing,
|
||||
KeyboardAvoidingView,
|
||||
Modal,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableWithoutFeedback,
|
||||
View,
|
||||
TextStyle
|
||||
} from 'react-native';
|
||||
import { Text, TextStyle } from 'react-native';
|
||||
import { BlockContext } from '@rocket.chat/ui-kit';
|
||||
|
||||
import Button from '../../Button';
|
||||
import { FormTextInput } from '../../TextInput';
|
||||
import { textParser } from '../utils';
|
||||
import { themes } from '../../../lib/constants';
|
||||
import I18n from '../../../i18n';
|
||||
import { isIOS } from '../../../lib/methods/helpers';
|
||||
import { useTheme } from '../../../theme';
|
||||
import { IText } from '../interfaces';
|
||||
import Chips from './Chips';
|
||||
import Items from './Items';
|
||||
import Input from './Input';
|
||||
import styles from './styles';
|
||||
import { useActionSheet } from '../../ActionSheet';
|
||||
import { MultiSelectContent } from './MultiSelectContent';
|
||||
|
||||
export interface IItemData {
|
||||
value: any;
|
||||
|
@ -38,7 +24,7 @@ interface IMultiSelect {
|
|||
context?: BlockContext;
|
||||
loading?: boolean;
|
||||
multiselect?: boolean;
|
||||
onSearch?: () => void;
|
||||
onSearch?: (keyword: string) => IItemData[] | Promise<IItemData[] | undefined>;
|
||||
onClose?: () => void;
|
||||
inputStyle?: TextStyle;
|
||||
value?: any[];
|
||||
|
@ -46,16 +32,6 @@ interface IMultiSelect {
|
|||
innerInputStyle?: object;
|
||||
}
|
||||
|
||||
const ANIMATION_DURATION = 200;
|
||||
const ANIMATION_PROPS = {
|
||||
duration: ANIMATION_DURATION,
|
||||
easing: Easing.inOut(Easing.quad),
|
||||
useNativeDriver: true
|
||||
};
|
||||
const animatedValue = new Animated.Value(0);
|
||||
|
||||
const behavior = isIOS ? 'padding' : null;
|
||||
|
||||
export const MultiSelect = React.memo(
|
||||
({
|
||||
options = [],
|
||||
|
@ -71,12 +47,11 @@ export const MultiSelect = React.memo(
|
|||
inputStyle,
|
||||
innerInputStyle
|
||||
}: IMultiSelect) => {
|
||||
const { theme } = useTheme();
|
||||
const [selected, select] = useState<any>(Array.isArray(values) ? values : []);
|
||||
const [open, setOpen] = useState(false);
|
||||
const [search, onSearchChange] = useState('');
|
||||
const { colors } = useTheme();
|
||||
const [selected, select] = useState<string[]>(Array.isArray(values) ? values : []);
|
||||
const [currentValue, setCurrentValue] = useState('');
|
||||
const [showContent, setShowContent] = useState(false);
|
||||
|
||||
const { showActionSheet, hideActionSheet } = useActionSheet();
|
||||
|
||||
useEffect(() => {
|
||||
if (Array.isArray(values)) {
|
||||
|
@ -84,10 +59,6 @@ export const MultiSelect = React.memo(
|
|||
}
|
||||
}, [values]);
|
||||
|
||||
useEffect(() => {
|
||||
setOpen(showContent);
|
||||
}, [showContent]);
|
||||
|
||||
useEffect(() => {
|
||||
if (values && values.length && !multiselect) {
|
||||
setCurrentValue(values[0].text);
|
||||
|
@ -95,19 +66,26 @@ export const MultiSelect = React.memo(
|
|||
}, []);
|
||||
|
||||
const onShow = () => {
|
||||
Animated.timing(animatedValue, {
|
||||
toValue: 1,
|
||||
...ANIMATION_PROPS
|
||||
}).start();
|
||||
setShowContent(true);
|
||||
showActionSheet({
|
||||
children: (
|
||||
<MultiSelectContent
|
||||
options={options}
|
||||
onSearch={onSearch}
|
||||
select={select}
|
||||
onChange={onChange}
|
||||
setCurrentValue={setCurrentValue}
|
||||
onHide={onHide}
|
||||
multiselect={multiselect}
|
||||
selectedItems={selected}
|
||||
/>
|
||||
),
|
||||
onClose,
|
||||
headerHeight: 275
|
||||
});
|
||||
};
|
||||
|
||||
const onHide = () => {
|
||||
onClose();
|
||||
Animated.timing(animatedValue, {
|
||||
toValue: 0,
|
||||
...ANIMATION_PROPS
|
||||
}).start(() => setShowContent(false));
|
||||
hideActionSheet();
|
||||
};
|
||||
|
||||
const onSelect = (item: IItemData) => {
|
||||
|
@ -127,45 +105,14 @@ export const MultiSelect = React.memo(
|
|||
} else {
|
||||
onChange({ value });
|
||||
setCurrentValue(text);
|
||||
onHide();
|
||||
}
|
||||
};
|
||||
|
||||
const renderContent = () => {
|
||||
const items: any = onSearch
|
||||
? options
|
||||
: options.filter((option: any) => textParser([option.text]).toLowerCase().includes(search.toLowerCase()));
|
||||
|
||||
return (
|
||||
<View style={[styles.modal, { backgroundColor: themes[theme].backgroundColor }]}>
|
||||
<View style={[styles.content, { backgroundColor: themes[theme].backgroundColor }]}>
|
||||
<FormTextInput
|
||||
testID='multi-select-search'
|
||||
onChangeText={onSearch || onSearchChange}
|
||||
placeholder={I18n.t('Search')}
|
||||
/>
|
||||
<Items items={items} selected={selected} onSelect={onSelect} theme={theme} />
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const translateY = animatedValue.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [600, 0]
|
||||
});
|
||||
|
||||
let button = multiselect ? (
|
||||
<Button title={`${selected.length} selecteds`} onPress={onShow} loading={loading} />
|
||||
) : (
|
||||
<Input
|
||||
onPress={onShow}
|
||||
theme={theme}
|
||||
loading={loading}
|
||||
disabled={disabled}
|
||||
inputStyle={inputStyle}
|
||||
innerInputStyle={innerInputStyle}>
|
||||
<Text style={[styles.pickerText, { color: currentValue ? themes[theme].titleText : themes[theme].auxiliaryText }]}>
|
||||
<Input onPress={onShow} loading={loading} disabled={disabled} inputStyle={inputStyle} innerInputStyle={innerInputStyle}>
|
||||
<Text style={[styles.pickerText, { color: currentValue ? colors.titleText : colors.auxiliaryText }]}>
|
||||
{currentValue || placeholder.text}
|
||||
</Text>
|
||||
</Input>
|
||||
|
@ -173,48 +120,18 @@ export const MultiSelect = React.memo(
|
|||
|
||||
if (context === BlockContext.FORM) {
|
||||
const items: any = options.filter((option: any) => selected.includes(option.value));
|
||||
|
||||
button = (
|
||||
<Input
|
||||
onPress={onShow}
|
||||
theme={theme}
|
||||
loading={loading}
|
||||
disabled={disabled}
|
||||
inputStyle={inputStyle}
|
||||
innerInputStyle={innerInputStyle}>
|
||||
<Input onPress={onShow} loading={loading} disabled={disabled} inputStyle={inputStyle} innerInputStyle={innerInputStyle}>
|
||||
{items.length ? (
|
||||
<Chips items={items} onSelect={(item: any) => (disabled ? {} : onSelect(item))} theme={theme} />
|
||||
<Chips items={items} onSelect={(item: any) => (disabled ? {} : onSelect(item))} />
|
||||
) : (
|
||||
<Text style={[styles.pickerText, { color: themes[theme].auxiliaryText }]}>{placeholder.text}</Text>
|
||||
<Text style={[styles.pickerText, { color: colors.auxiliaryText }]}>{placeholder.text}</Text>
|
||||
)}
|
||||
</Input>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal animationType='fade' transparent visible={open} onRequestClose={onHide} onShow={onShow}>
|
||||
<TouchableWithoutFeedback onPress={onHide}>
|
||||
<View style={styles.container}>
|
||||
<View
|
||||
style={[
|
||||
StyleSheet.absoluteFill,
|
||||
{
|
||||
opacity: themes[theme].backdropOpacity,
|
||||
backgroundColor: themes[theme].backdropColor
|
||||
}
|
||||
]}
|
||||
/>
|
||||
{/* @ts-ignore*/}
|
||||
<KeyboardAvoidingView style={styles.keyboardView} behavior={behavior}>
|
||||
<Animated.View style={[styles.animatedContent, { transform: [{ translateY }] }]}>
|
||||
{showContent ? renderContent() : null}
|
||||
</Animated.View>
|
||||
</KeyboardAvoidingView>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
</Modal>
|
||||
{button}
|
||||
</>
|
||||
);
|
||||
return <>{button}</>;
|
||||
}
|
||||
);
|
||||
|
|
|
@ -2,18 +2,15 @@ import { StyleSheet } from 'react-native';
|
|||
|
||||
import sharedStyles from '../../../views/Styles';
|
||||
|
||||
export default StyleSheet.create<any>({
|
||||
export default StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-end'
|
||||
},
|
||||
modal: {
|
||||
height: 300,
|
||||
width: '100%',
|
||||
borderTopRightRadius: 16,
|
||||
borderTopLeftRadius: 16,
|
||||
overflow: 'hidden'
|
||||
actionSheetContainer: {
|
||||
padding: 16,
|
||||
flex: 1
|
||||
},
|
||||
content: {
|
||||
padding: 16
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import React, { useState } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Text } from 'react-native';
|
||||
|
||||
import { themes } from '../../lib/constants';
|
||||
import { MultiSelect } from '../../containers/UIKit/MultiSelect';
|
||||
import { ISearchLocal } from '../../definitions';
|
||||
import I18n from '../../i18n';
|
||||
|
@ -9,7 +8,8 @@ import { getAvatarURL } from '../../lib/methods/helpers/getAvatarUrl';
|
|||
import { ICreateDiscussionViewSelectChannel } from './interfaces';
|
||||
import styles from './styles';
|
||||
import { localSearch } from '../../lib/methods';
|
||||
import { getRoomAvatar, getRoomTitle, debounce } from '../../lib/methods/helpers';
|
||||
import { getRoomAvatar, getRoomTitle } from '../../lib/methods/helpers';
|
||||
import { useTheme } from '../../theme';
|
||||
|
||||
const SelectChannel = ({
|
||||
server,
|
||||
|
@ -18,19 +18,28 @@ const SelectChannel = ({
|
|||
onChannelSelect,
|
||||
initial,
|
||||
blockUnauthenticatedAccess,
|
||||
serverVersion,
|
||||
theme
|
||||
serverVersion
|
||||
}: ICreateDiscussionViewSelectChannel): React.ReactElement => {
|
||||
const [channels, setChannels] = useState<ISearchLocal[]>([]);
|
||||
const { colors } = useTheme();
|
||||
|
||||
const getChannels = debounce(async (keyword = '') => {
|
||||
const getChannels = async (keyword = '') => {
|
||||
try {
|
||||
const res = (await localSearch({ text: keyword })) as ISearchLocal[];
|
||||
const res = (await localSearch({ text: keyword, filterUsers: false })) as ISearchLocal[];
|
||||
setChannels(res);
|
||||
return res.map(channel => ({
|
||||
value: channel,
|
||||
text: { text: getRoomTitle(channel) },
|
||||
imageUrl: getAvatar(channel)
|
||||
}));
|
||||
} catch {
|
||||
// do nothing
|
||||
}
|
||||
}, 300);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
getChannels('');
|
||||
}, []);
|
||||
|
||||
const getAvatar = (item: ISearchLocal) =>
|
||||
getAvatarURL({
|
||||
|
@ -47,7 +56,7 @@ const SelectChannel = ({
|
|||
|
||||
return (
|
||||
<>
|
||||
<Text style={[styles.label, { color: themes[theme].titleText }]}>{I18n.t('Parent_channel_or_group')}</Text>
|
||||
<Text style={[styles.label, { color: colors.titleText }]}>{I18n.t('Parent_channel_or_group')}</Text>
|
||||
<MultiSelect
|
||||
inputStyle={styles.inputStyle}
|
||||
onChange={onChannelSelect}
|
||||
|
@ -59,7 +68,7 @@ const SelectChannel = ({
|
|||
text: { text: getRoomTitle(channel) },
|
||||
imageUrl: getAvatar(channel)
|
||||
}))}
|
||||
onClose={() => setChannels([])}
|
||||
onClose={() => getChannels('')}
|
||||
placeholder={{ text: `${I18n.t('Select_a_Channel')}...` }}
|
||||
/>
|
||||
</>
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
import React, { useState } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Text } from 'react-native';
|
||||
import { BlockContext } from '@rocket.chat/ui-kit';
|
||||
|
||||
import { getAvatarURL } from '../../lib/methods/helpers/getAvatarUrl';
|
||||
import I18n from '../../i18n';
|
||||
import { MultiSelect } from '../../containers/UIKit/MultiSelect';
|
||||
import { themes } from '../../lib/constants';
|
||||
import styles from './styles';
|
||||
import { ICreateDiscussionViewSelectUsers } from './interfaces';
|
||||
import { SubscriptionType, IUser } from '../../definitions';
|
||||
import { search } from '../../lib/methods';
|
||||
import { getRoomAvatar, getRoomTitle, debounce } from '../../lib/methods/helpers';
|
||||
import { getRoomAvatar, getRoomTitle } from '../../lib/methods/helpers';
|
||||
import { useTheme } from '../../theme';
|
||||
|
||||
const SelectUsers = ({
|
||||
server,
|
||||
|
@ -19,22 +19,31 @@ const SelectUsers = ({
|
|||
selected,
|
||||
onUserSelect,
|
||||
blockUnauthenticatedAccess,
|
||||
serverVersion,
|
||||
theme
|
||||
serverVersion
|
||||
}: ICreateDiscussionViewSelectUsers): React.ReactElement => {
|
||||
const [users, setUsers] = useState<any[]>([]);
|
||||
const { colors } = useTheme();
|
||||
|
||||
const getUsers = debounce(async (keyword = '') => {
|
||||
const getUsers = async (keyword = '') => {
|
||||
try {
|
||||
const res = await search({ text: keyword, filterRooms: false });
|
||||
const selectedUsers = users.filter((u: IUser) => selected.includes(u.name));
|
||||
const filteredUsers = res.filter(r => !selectedUsers.find((u: IUser) => u.name === r.name));
|
||||
const items = [...selectedUsers, ...filteredUsers];
|
||||
setUsers(items);
|
||||
return items.map((user: IUser) => ({
|
||||
value: user.name,
|
||||
text: { text: getRoomTitle(user) },
|
||||
imageUrl: getAvatar(user)
|
||||
}));
|
||||
} catch {
|
||||
// do nothing
|
||||
}
|
||||
}, 300);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
getUsers('');
|
||||
}, []);
|
||||
|
||||
const getAvatar = (item: IUser) =>
|
||||
getAvatarURL({
|
||||
|
@ -50,7 +59,7 @@ const SelectUsers = ({
|
|||
|
||||
return (
|
||||
<>
|
||||
<Text style={[styles.label, { color: themes[theme].titleText }]}>{I18n.t('Invite_users')}</Text>
|
||||
<Text style={[styles.label, { color: colors.titleText }]}>{I18n.t('Invite_users')}</Text>
|
||||
<MultiSelect
|
||||
inputStyle={styles.inputStyle}
|
||||
onSearch={getUsers}
|
||||
|
@ -60,7 +69,6 @@ const SelectUsers = ({
|
|||
text: { text: getRoomTitle(user) },
|
||||
imageUrl: getAvatar(user)
|
||||
}))}
|
||||
onClose={() => setUsers(users.filter((u: IUser) => selected.includes(u.name)))}
|
||||
placeholder={{ text: `${I18n.t('Select_Users')}...` }}
|
||||
context={BlockContext.FORM}
|
||||
multiselect
|
||||
|
|
|
@ -164,7 +164,6 @@ class CreateChannelView extends React.Component<ICreateChannelViewProps, ICreate
|
|||
onChannelSelect={this.selectChannel}
|
||||
blockUnauthenticatedAccess={blockUnauthenticatedAccess}
|
||||
serverVersion={serverVersion}
|
||||
theme={theme}
|
||||
/>
|
||||
<FormTextInput
|
||||
label={I18n.t('Discussion_name')}
|
||||
|
@ -182,7 +181,6 @@ class CreateChannelView extends React.Component<ICreateChannelViewProps, ICreate
|
|||
onUserSelect={this.selectUsers}
|
||||
blockUnauthenticatedAccess={blockUnauthenticatedAccess}
|
||||
serverVersion={serverVersion}
|
||||
theme={theme}
|
||||
/>
|
||||
{this.isEncryptionEnabled ? (
|
||||
<>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { NewMessageStackParamList } from '../../stacks/types';
|
||||
import { ISubscription, SubscriptionType } from '../../definitions/ISubscription';
|
||||
import { IBaseScreen, IMessage, ISearchLocal, IUser } from '../../definitions';
|
||||
import { TSupportedThemes } from '../../theme';
|
||||
|
||||
export interface IResult {
|
||||
rid: string;
|
||||
|
@ -43,7 +42,6 @@ export interface ICreateDiscussionViewSelectChannel {
|
|||
onChannelSelect: Function;
|
||||
blockUnauthenticatedAccess: boolean;
|
||||
serverVersion: string;
|
||||
theme: TSupportedThemes;
|
||||
}
|
||||
|
||||
export interface ICreateDiscussionViewSelectUsers {
|
||||
|
@ -54,5 +52,4 @@ export interface ICreateDiscussionViewSelectUsers {
|
|||
onUserSelect: Function;
|
||||
blockUnauthenticatedAccess: boolean;
|
||||
serverVersion: string;
|
||||
theme: TSupportedThemes;
|
||||
}
|
||||
|
|
|
@ -150,9 +150,9 @@ const ForwardLivechatView = ({ navigation, route }: IBaseScreen<ChatsStackParamL
|
|||
|
||||
return (
|
||||
<View style={[styles.container, { backgroundColor: colors.auxiliaryBackground }]}>
|
||||
<Input onPress={onPressDepartment} placeholder={I18n.t('Select_a_Department')} theme={theme} />
|
||||
<Input onPress={onPressDepartment} placeholder={I18n.t('Select_a_Department')} />
|
||||
<OrSeparator theme={theme} />
|
||||
<Input onPress={onPressUser} placeholder={I18n.t('Select_a_User')} theme={theme} />
|
||||
<Input onPress={onPressUser} placeholder={I18n.t('Select_a_User')} />
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue