New reaction picker as bottom action sheet
This commit is contained in:
parent
de9036edeb
commit
b05d876946
|
@ -140,8 +140,8 @@ const ActionSheet = React.memo(
|
||||||
style={{ ...styles.container, ...bottomSheet }}
|
style={{ ...styles.container, ...bottomSheet }}
|
||||||
backgroundStyle={{ backgroundColor: colors.focusedBackground }}
|
backgroundStyle={{ backgroundColor: colors.focusedBackground }}
|
||||||
onChange={index => index === -1 && onClose()}
|
onChange={index => index === -1 && onClose()}
|
||||||
// We need this to allow horizontal swipe gestures inside bottom sheet like in reaction picker
|
activeOffsetY={[-1, 1]}
|
||||||
enableContentPanningGesture={data?.enableContentPanningGesture ?? true}
|
failOffsetX={[-5, 5]}
|
||||||
{...androidTablet}
|
{...androidTablet}
|
||||||
>
|
>
|
||||||
<BottomSheetContent options={data?.options} hide={hide} children={data?.children} hasCancel={data?.hasCancel} />
|
<BottomSheetContent options={data?.options} hide={hide} children={data?.children} hasCancel={data?.hasCancel} />
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { FlatList, Text, TouchableOpacity } from 'react-native';
|
import { Text, TouchableOpacity } from 'react-native';
|
||||||
|
import { BottomSheetFlatList } from '@gorhom/bottom-sheet';
|
||||||
|
import { FlatList as GHFlatList } from 'react-native-gesture-handler';
|
||||||
|
|
||||||
import shortnameToUnicode from '../../lib/methods/helpers/shortnameToUnicode';
|
import shortnameToUnicode from '../../lib/methods/helpers/shortnameToUnicode';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
|
@ -10,7 +12,7 @@ import { IEmoji, IEmojiCategory } from '../../definitions/IEmoji';
|
||||||
const MAX_EMOJI_SIZE = 50;
|
const MAX_EMOJI_SIZE = 50;
|
||||||
|
|
||||||
const renderEmoji = (emoji: IEmoji, size: number, baseUrl: string) => {
|
const renderEmoji = (emoji: IEmoji, size: number, baseUrl: string) => {
|
||||||
if (emoji && emoji.isCustom) {
|
if (emoji?.isCustom || emoji?.name) {
|
||||||
return (
|
return (
|
||||||
<CustomEmoji
|
<CustomEmoji
|
||||||
style={[styles.customCategoryEmoji, { height: size - 16, width: size - 16 }]}
|
style={[styles.customCategoryEmoji, { height: size - 16, width: size - 16 }]}
|
||||||
|
@ -26,16 +28,21 @@ const renderEmoji = (emoji: IEmoji, size: number, baseUrl: string) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const EmojiCategory = React.memo(({ baseUrl, onEmojiSelected, emojis, width, tabsCount, ...props }: IEmojiCategory) => {
|
const EmojiCategory = React.memo(
|
||||||
|
({ baseUrl, onEmojiSelected, emojis, width, tabsCount, isBottomSheet, ...props }: IEmojiCategory) => {
|
||||||
const emojiSize = width ? Math.min(width / tabsCount, MAX_EMOJI_SIZE) : MAX_EMOJI_SIZE;
|
const emojiSize = width ? Math.min(width / tabsCount, MAX_EMOJI_SIZE) : MAX_EMOJI_SIZE;
|
||||||
const numColumns = Math.trunc(width ? width / emojiSize : tabsCount);
|
const numColumns = Math.trunc(width ? width / emojiSize : tabsCount);
|
||||||
|
|
||||||
|
const FlatList = isBottomSheet ? BottomSheetFlatList : GHFlatList;
|
||||||
|
|
||||||
const renderItem = (emoji: IEmoji) => (
|
const renderItem = (emoji: IEmoji) => (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
activeOpacity={0.7}
|
activeOpacity={0.7}
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
key={emoji && emoji.isCustom ? emoji.content : emoji}
|
key={emoji && emoji.isCustom ? emoji.content : emoji}
|
||||||
onPress={() => onEmojiSelected(emoji)}
|
onPress={() => onEmojiSelected(emoji)}
|
||||||
testID={`reaction-picker-${emoji && emoji.isCustom ? emoji.content : emoji}`}>
|
testID={`reaction-picker-${emoji && emoji.isCustom ? emoji.content : emoji}`}
|
||||||
|
>
|
||||||
{renderEmoji(emoji, emojiSize, baseUrl)}
|
{renderEmoji(emoji, emojiSize, baseUrl)}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
);
|
);
|
||||||
|
@ -60,6 +67,7 @@ const EmojiCategory = React.memo(({ baseUrl, onEmojiSelected, emojis, width, tab
|
||||||
keyboardDismissMode={'none'}
|
keyboardDismissMode={'none'}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
export default EmojiCategory;
|
export default EmojiCategory;
|
||||||
|
|
|
@ -14,19 +14,11 @@ const TabBar = React.memo(({ activeTab, tabs, goToPage, tabEmojiStyle }: ITabBar
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
activeOpacity={0.7}
|
activeOpacity={0.7}
|
||||||
key={tab}
|
key={tab}
|
||||||
onPress={() => {
|
onPress={() => goToPage?.(i)}
|
||||||
if (goToPage) {
|
|
||||||
goToPage(i);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
style={styles.tab}
|
style={styles.tab}
|
||||||
testID={`reaction-picker-${tab}`}>
|
testID={`reaction-picker-${tab}`}>
|
||||||
<Text style={[styles.tabEmoji, tabEmojiStyle]}>{tab}</Text>
|
<Text style={[styles.tabEmoji, tabEmojiStyle]}>{tab}</Text>
|
||||||
{activeTab === i ? (
|
<View style={activeTab === i ? [styles.activeTabLine, { backgroundColor: colors.tintColor }] : styles.tabLine} />
|
||||||
<View style={[styles.activeTabLine, { backgroundColor: colors.tintColor }]} />
|
|
||||||
) : (
|
|
||||||
<View style={styles.tabLine} />
|
|
||||||
)}
|
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
))}
|
))}
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -19,14 +19,15 @@ import { IEmoji, ICustomEmojis, TFrequentlyUsedEmojiModel } from '../../definiti
|
||||||
import { useAppSelector } from '../../lib/hooks';
|
import { useAppSelector } from '../../lib/hooks';
|
||||||
import { IEmojiPickerProps, EventTypes } from './interfaces';
|
import { IEmojiPickerProps, EventTypes } from './interfaces';
|
||||||
|
|
||||||
const EmojiPicker = React.memo(({ onItemClicked, tabEmojiStyle, isEmojiKeyboard = false }: IEmojiPickerProps) => {
|
const EmojiPicker = React.memo(
|
||||||
|
({ onItemClicked, tabEmojiStyle, isEmojiKeyboard = false, searching = false, searchedEmojis = [] }: IEmojiPickerProps) => {
|
||||||
const [frequentlyUsed, setFrequentlyUsed] = useState<IEmoji[]>([]);
|
const [frequentlyUsed, setFrequentlyUsed] = useState<IEmoji[]>([]);
|
||||||
const [show, setShow] = useState(false);
|
const [show, setShow] = useState(false);
|
||||||
const [width, setWidth] = useState(null);
|
const [width, setWidth] = useState(null);
|
||||||
const { colors } = useTheme();
|
const { colors } = useTheme();
|
||||||
|
|
||||||
|
const baseUrl = useAppSelector(state => state.server?.server);
|
||||||
const allCustomEmojis: ICustomEmojis = useAppSelector(state => state.customEmojis);
|
const allCustomEmojis: ICustomEmojis = useAppSelector(state => state.customEmojis);
|
||||||
const baseUrl = useAppSelector(state=>state.server?.server)
|
|
||||||
const customEmojis = Object.keys(allCustomEmojis)
|
const customEmojis = Object.keys(allCustomEmojis)
|
||||||
.filter(item => item === allCustomEmojis[item].name)
|
.filter(item => item === allCustomEmojis[item].name)
|
||||||
.map(item => ({
|
.map(item => ({
|
||||||
|
@ -56,7 +57,7 @@ const EmojiPicker = React.memo(({ onItemClicked, tabEmojiStyle, isEmojiKeyboard
|
||||||
const content = emoji;
|
const content = emoji;
|
||||||
_addFrequentlyUsed({ content, isCustom: false });
|
_addFrequentlyUsed({ content, isCustom: false });
|
||||||
const shortname = `:${emoji}:`;
|
const shortname = `:${emoji}:`;
|
||||||
onItemClicked(EventTypes.EMOJI_PRESSED, shortnameToUnicode(shortname));
|
onItemClicked(EventTypes.EMOJI_PRESSED, shortnameToUnicode(shortname), shortname);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log(e);
|
log(e);
|
||||||
|
@ -127,6 +128,7 @@ const EmojiPicker = React.memo(({ onItemClicked, tabEmojiStyle, isEmojiKeyboard
|
||||||
baseUrl={baseUrl}
|
baseUrl={baseUrl}
|
||||||
tabLabel={label}
|
tabLabel={label}
|
||||||
tabsCount={tabsCount}
|
tabsCount={tabsCount}
|
||||||
|
isBottomSheet={!isEmojiKeyboard}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -139,19 +141,33 @@ const EmojiPicker = React.memo(({ onItemClicked, tabEmojiStyle, isEmojiKeyboard
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View onLayout={onLayout} style={{ flex: 1 }}>
|
<View onLayout={onLayout} style={{ flex: 1 }}>
|
||||||
|
{searching ? (
|
||||||
|
<EmojiCategory
|
||||||
|
emojis={searchedEmojis as IEmoji[]}
|
||||||
|
onEmojiSelected={(emoji: IEmoji) => handleEmojiSelect(emoji)}
|
||||||
|
style={styles.categoryContainer}
|
||||||
|
width={width}
|
||||||
|
baseUrl={baseUrl}
|
||||||
|
tabLabel={'searching'}
|
||||||
|
tabsCount={tabsCount}
|
||||||
|
isBottomSheet={!isEmojiKeyboard}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
<ScrollableTabView
|
<ScrollableTabView
|
||||||
renderTabBar={() => <TabBar tabEmojiStyle={tabEmojiStyle} />}
|
renderTabBar={() => <TabBar tabEmojiStyle={tabEmojiStyle} />}
|
||||||
contentProps={{
|
contentProps={{
|
||||||
keyboardShouldPersistTaps: 'always',
|
keyboardShouldPersistTaps: 'always',
|
||||||
keyboardDismissMode: 'none'
|
keyboardDismissMode: 'none'
|
||||||
}}
|
}}
|
||||||
style={{ backgroundColor: colors.focusedBackground }}>
|
style={{ backgroundColor: colors.focusedBackground }}
|
||||||
|
>
|
||||||
{categories.tabs.map((tab: any, i) =>
|
{categories.tabs.map((tab: any, i) =>
|
||||||
i === 0 && frequentlyUsed.length === 0
|
i === 0 && frequentlyUsed.length === 0
|
||||||
? null // when no frequentlyUsed don't show the tab
|
? null // when no frequentlyUsed don't show the tab
|
||||||
: renderCategory(tab.category, i, tab.tabLabel, tabsCount)
|
: renderCategory(tab.category, i, tab.tabLabel, tabsCount)
|
||||||
)}
|
)}
|
||||||
</ScrollableTabView>
|
</ScrollableTabView>
|
||||||
|
)}
|
||||||
{isEmojiKeyboard && (
|
{isEmojiKeyboard && (
|
||||||
<Footer
|
<Footer
|
||||||
onSearchPressed={() => onItemClicked(EventTypes.SEARCH_PRESSED)}
|
onSearchPressed={() => onItemClicked(EventTypes.SEARCH_PRESSED)}
|
||||||
|
@ -160,6 +176,7 @@ const EmojiPicker = React.memo(({ onItemClicked, tabEmojiStyle, isEmojiKeyboard
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
export default EmojiPicker;
|
export default EmojiPicker;
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import { StyleProp, TextStyle } from 'react-native';
|
import { StyleProp, TextStyle } from 'react-native';
|
||||||
|
|
||||||
|
import { IEmoji } from '../../definitions';
|
||||||
|
|
||||||
export enum EventTypes {
|
export enum EventTypes {
|
||||||
EMOJI_PRESSED = 'emojiPressed',
|
EMOJI_PRESSED = 'emojiPressed',
|
||||||
BACKSPACE_PRESSED = 'backspacePressed',
|
BACKSPACE_PRESSED = 'backspacePressed',
|
||||||
|
@ -7,9 +9,11 @@ export enum EventTypes {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IEmojiPickerProps {
|
export interface IEmojiPickerProps {
|
||||||
onItemClicked: (event: EventTypes, emoji?: string) => void;
|
onItemClicked: (event: EventTypes, emoji?: string, shortname?: string) => void;
|
||||||
tabEmojiStyle?: StyleProp<TextStyle>;
|
tabEmojiStyle?: StyleProp<TextStyle>;
|
||||||
isEmojiKeyboard?: boolean;
|
isEmojiKeyboard?: boolean;
|
||||||
|
searching?: boolean;
|
||||||
|
searchedEmojis?: (string | IEmoji)[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IFooterProps {
|
export interface IFooterProps {
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react';
|
||||||
import { View, Text, TouchableOpacity, TextInput, FlatList } from 'react-native';
|
import { View, Text, TouchableOpacity, TextInput, FlatList } from 'react-native';
|
||||||
import { orderBy } from 'lodash';
|
import { orderBy } from 'lodash';
|
||||||
|
|
||||||
import FormTextInput from '../TextInput/FormTextInput';
|
import { FormTextInput } from '../TextInput/FormTextInput';
|
||||||
import { useTheme } from '../../theme';
|
import { useTheme } from '../../theme';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import { CustomIcon } from '../CustomIcon';
|
import { CustomIcon } from '../CustomIcon';
|
||||||
|
@ -31,7 +31,7 @@ const renderEmoji = (emoji: IEmoji, size: number, baseUrl: string) => {
|
||||||
|
|
||||||
const EmojiSearchbar = React.forwardRef<TextInput, IEmojiSearchbarProps>(
|
const EmojiSearchbar = React.forwardRef<TextInput, IEmojiSearchbarProps>(
|
||||||
({ openEmoji, onChangeText, emojis, onEmojiSelected, baseUrl }, ref) => {
|
({ openEmoji, onChangeText, emojis, onEmojiSelected, baseUrl }, ref) => {
|
||||||
const { colors, theme } = useTheme();
|
const { colors } = useTheme();
|
||||||
const [searchText, setSearchText] = useState<string>('');
|
const [searchText, setSearchText] = useState<string>('');
|
||||||
const [frequentlyUsed, setFrequentlyUsed] = useState([]);
|
const [frequentlyUsed, setFrequentlyUsed] = useState([]);
|
||||||
|
|
||||||
|
@ -103,7 +103,6 @@ const EmojiSearchbar = React.forwardRef<TextInput, IEmojiSearchbarProps>(
|
||||||
style={[styles.emojiSearchbar, { backgroundColor: colors.passcodeButtonActive }]}
|
style={[styles.emojiSearchbar, { backgroundColor: colors.passcodeButtonActive }]}
|
||||||
containerStyle={styles.textInputContainer}
|
containerStyle={styles.textInputContainer}
|
||||||
value={searchText}
|
value={searchText}
|
||||||
theme={theme}
|
|
||||||
onClearInput={() => handleTextChange('')}
|
onClearInput={() => handleTextChange('')}
|
||||||
iconRight={'search'}
|
iconRight={'search'}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -36,6 +36,7 @@ export interface IEmojiCategory {
|
||||||
style: StyleProp<ImageStyle>;
|
style: StyleProp<ImageStyle>;
|
||||||
tabLabel: string;
|
tabLabel: string;
|
||||||
tabsCount: number;
|
tabsCount: number;
|
||||||
|
isBottomSheet: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TGetCustomEmoji = (name: string) => any;
|
export type TGetCustomEmoji = (name: string) => any;
|
||||||
|
|
|
@ -1,87 +1,86 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { View } from 'react-native';
|
import { View } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { Q } from '@nozbe/watermelondb';
|
||||||
import Modal from 'react-native-modal';
|
|
||||||
|
|
||||||
import EmojiPicker from '../../containers/EmojiPicker';
|
import EmojiPicker from '../../containers/EmojiPicker';
|
||||||
import { isAndroid } from '../../lib/methods/helpers';
|
import { useTheme } from '../../theme';
|
||||||
import { themes } from '../../lib/constants';
|
|
||||||
import { TSupportedThemes, withTheme } from '../../theme';
|
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { IApplicationState } from '../../definitions';
|
|
||||||
import { EventTypes } from '../../containers/EmojiPicker/interfaces';
|
import { EventTypes } from '../../containers/EmojiPicker/interfaces';
|
||||||
|
import { FormTextInput } from '../../containers/TextInput/FormTextInput';
|
||||||
const margin = isAndroid ? 40 : 20;
|
import I18n from '../../i18n';
|
||||||
const maxSize = 400;
|
import { sanitizeLikeString } from '../../lib/database/utils';
|
||||||
|
import { emojis } from '../../containers/EmojiPicker/emojis';
|
||||||
|
import database from '../../lib/database';
|
||||||
|
import { debounce } from '../../lib/methods/helpers/debounce';
|
||||||
|
|
||||||
interface IReactionPickerProps {
|
interface IReactionPickerProps {
|
||||||
message?: any;
|
message?: any;
|
||||||
show: boolean;
|
show: boolean;
|
||||||
isMasterDetail: boolean;
|
|
||||||
reactionClose: () => void;
|
reactionClose: () => void;
|
||||||
onEmojiSelected: (shortname: string, id: string) => void;
|
onEmojiSelected: (shortname: string, id: string) => void;
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
theme: TSupportedThemes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ReactionPicker extends React.Component<IReactionPickerProps> {
|
const MAX_EMOJIS_TO_DISPLAY = 20;
|
||||||
shouldComponentUpdate(nextProps: IReactionPickerProps) {
|
|
||||||
const { show, width, height } = this.props;
|
|
||||||
return nextProps.show !== show || width !== nextProps.width || height !== nextProps.height;
|
|
||||||
}
|
|
||||||
|
|
||||||
onEmojiSelected = (_eventType: EventTypes, emoji?: string, shortname?: string) => {
|
const ReactionPicker = React.memo(({ onEmojiSelected, message, reactionClose }: IReactionPickerProps) => {
|
||||||
|
const { colors } = useTheme();
|
||||||
|
const [searchText, setSearchText] = React.useState<string>('');
|
||||||
|
const [searchedEmojis, setSearchedEmojis] = React.useState<any[]>([]);
|
||||||
|
const [searching, setSearching] = React.useState<boolean>(false);
|
||||||
|
|
||||||
|
const handleTextChange = (text: string) => {
|
||||||
|
setSearching(text !== '');
|
||||||
|
setSearchText(text);
|
||||||
|
searchEmojis(text);
|
||||||
|
};
|
||||||
|
|
||||||
|
const searchEmojis = debounce(async (keyword: string) => {
|
||||||
|
const likeString = sanitizeLikeString(keyword);
|
||||||
|
const whereClause = [];
|
||||||
|
if (likeString) {
|
||||||
|
whereClause.push(Q.where('name', Q.like(`${likeString}%`)));
|
||||||
|
}
|
||||||
|
const db = database.active;
|
||||||
|
const customEmojisCollection = db.get('custom_emojis');
|
||||||
|
const customEmojis = await (await customEmojisCollection.query(...whereClause).fetch()).slice(0, MAX_EMOJIS_TO_DISPLAY / 2);
|
||||||
|
const filteredEmojis = emojis.filter(emoji => emoji.indexOf(keyword) !== -1).slice(0, MAX_EMOJIS_TO_DISPLAY / 2);
|
||||||
|
const mergedEmojis = [...customEmojis, ...filteredEmojis];
|
||||||
|
setSearchedEmojis(mergedEmojis);
|
||||||
|
}, 300);
|
||||||
|
|
||||||
|
const handleEmojiSelect = (_eventType: EventTypes, emoji?: string, shortname?: string) => {
|
||||||
// standard emojis: `emoji` is unicode and `shortname` is :joy:
|
// standard emojis: `emoji` is unicode and `shortname` is :joy:
|
||||||
// custom emojis: only `emoji` is returned with shortname type (:joy:)
|
// custom emojis: only `emoji` is returned with shortname type (:joy:)
|
||||||
// to set reactions, we need shortname type
|
// to set reactions, we need shortname type
|
||||||
const { onEmojiSelected, message } = this.props;
|
|
||||||
if (message) {
|
if (message) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
onEmojiSelected(shortname || emoji, message.id);
|
onEmojiSelected(shortname || emoji, message.id);
|
||||||
}
|
}
|
||||||
|
reactionClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
return (
|
||||||
const { width, height, show, reactionClose, isMasterDetail, theme } = this.props;
|
<View style={[styles.reactionPickerContainer]} testID='reaction-picker'>
|
||||||
|
<View style={styles.searchbarContainer}>
|
||||||
let widthStyle = width - margin;
|
<FormTextInput
|
||||||
let heightStyle = Math.min(width, height) - margin * 2;
|
autoCapitalize='none'
|
||||||
|
autoCorrect={false}
|
||||||
if (isMasterDetail) {
|
blurOnSubmit
|
||||||
widthStyle = maxSize;
|
placeholder={I18n.t('Search_emoji')}
|
||||||
heightStyle = maxSize;
|
returnKeyType='search'
|
||||||
}
|
underlineColorAndroid='transparent'
|
||||||
|
onChangeText={handleTextChange}
|
||||||
return show ? (
|
style={[styles.reactionPickerSearchbar, { backgroundColor: colors.passcodeButtonActive }]}
|
||||||
<Modal
|
value={searchText}
|
||||||
isVisible={show}
|
onClearInput={() => handleTextChange('')}
|
||||||
style={{ alignItems: 'center' }}
|
iconRight={'search'}
|
||||||
onBackdropPress={reactionClose}
|
/>
|
||||||
onBackButtonPress={reactionClose}
|
|
||||||
animationIn='fadeIn'
|
|
||||||
animationOut='fadeOut'
|
|
||||||
backdropOpacity={themes[theme].backdropOpacity}
|
|
||||||
>
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
styles.reactionPickerContainer,
|
|
||||||
{
|
|
||||||
width: widthStyle,
|
|
||||||
height: heightStyle
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
testID='reaction-picker'
|
|
||||||
>
|
|
||||||
<EmojiPicker onItemClicked={this.onEmojiSelected} />
|
|
||||||
</View>
|
</View>
|
||||||
</Modal>
|
<EmojiPicker onItemClicked={handleEmojiSelect} searching={searching} searchedEmojis={searchedEmojis} />
|
||||||
) : null;
|
</View>
|
||||||
}
|
);
|
||||||
}
|
|
||||||
|
|
||||||
const mapStateToProps = (state: IApplicationState) => ({
|
|
||||||
isMasterDetail: state.app.isMasterDetail
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps)(withTheme(ReactionPicker));
|
export default ReactionPicker;
|
||||||
|
|
|
@ -828,12 +828,32 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
|
||||||
this.setState({ selectedMessage: undefined, replying: false, replyWithMention: false });
|
this.setState({ selectedMessage: undefined, replying: false, replyWithMention: false });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
showReactionPicker = () => {
|
||||||
|
const { showActionSheet, width, height } = this.props;
|
||||||
|
const { reacting, selectedMessage } = this.state;
|
||||||
|
showActionSheet &&
|
||||||
|
showActionSheet({
|
||||||
|
children: (
|
||||||
|
<ReactionPicker
|
||||||
|
show={reacting}
|
||||||
|
message={selectedMessage}
|
||||||
|
onEmojiSelected={this.onReactionPress}
|
||||||
|
reactionClose={this.onReactionClose}
|
||||||
|
width={width}
|
||||||
|
height={height}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
snaps: [400, '100%']
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
onReactionInit = (message: TAnyMessageModel) => {
|
onReactionInit = (message: TAnyMessageModel) => {
|
||||||
this.setState({ selectedMessage: message, reacting: true });
|
this.setState({ selectedMessage: message }, this.showReactionPicker);
|
||||||
};
|
};
|
||||||
|
|
||||||
onReactionClose = () => {
|
onReactionClose = () => {
|
||||||
this.setState({ selectedMessage: undefined, reacting: false });
|
const { hideActionSheet } = this.props;
|
||||||
|
this.setState({ selectedMessage: undefined, reacting: false }, hideActionSheet);
|
||||||
};
|
};
|
||||||
|
|
||||||
onMessageLongPress = (message: TAnyMessageModel) => {
|
onMessageLongPress = (message: TAnyMessageModel) => {
|
||||||
|
@ -1493,8 +1513,8 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
console.count(`${this.constructor.name}.render calls`);
|
console.count(`${this.constructor.name}.render calls`);
|
||||||
const { room, selectedMessage, loading, reacting } = this.state;
|
const { room, loading } = this.state;
|
||||||
const { user, baseUrl, theme, navigation, Hide_System_Messages, width, height, serverVersion } = this.props;
|
const { user, baseUrl, theme, navigation, Hide_System_Messages, width, serverVersion } = this.props;
|
||||||
const { rid, t } = room;
|
const { rid, t } = room;
|
||||||
let sysMes;
|
let sysMes;
|
||||||
let bannerClosed;
|
let bannerClosed;
|
||||||
|
@ -1526,15 +1546,6 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
|
||||||
/>
|
/>
|
||||||
{this.renderFooter()}
|
{this.renderFooter()}
|
||||||
{this.renderActions()}
|
{this.renderActions()}
|
||||||
<ReactionPicker
|
|
||||||
show={reacting}
|
|
||||||
message={selectedMessage}
|
|
||||||
onEmojiSelected={this.onReactionPress}
|
|
||||||
reactionClose={this.onReactionClose}
|
|
||||||
width={width}
|
|
||||||
height={height}
|
|
||||||
theme={theme}
|
|
||||||
/>
|
|
||||||
<UploadProgress rid={rid} user={user} baseUrl={baseUrl} width={width} />
|
<UploadProgress rid={rid} user={user} baseUrl={baseUrl} width={width} />
|
||||||
<JoinCode ref={this.joinCode} onJoin={this.onJoin} rid={rid} t={t} theme={theme} />
|
<JoinCode ref={this.joinCode} onJoin={this.onJoin} rid={rid} t={t} theme={theme} />
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
|
|
|
@ -15,9 +15,7 @@ export default StyleSheet.create({
|
||||||
marginVertical: 15
|
marginVertical: 15
|
||||||
},
|
},
|
||||||
reactionPickerContainer: {
|
reactionPickerContainer: {
|
||||||
borderRadius: 4,
|
height: '100%'
|
||||||
flexDirection: 'column',
|
|
||||||
overflow: 'hidden'
|
|
||||||
},
|
},
|
||||||
bannerContainer: {
|
bannerContainer: {
|
||||||
paddingVertical: 12,
|
paddingVertical: 12,
|
||||||
|
@ -64,5 +62,12 @@ export default StyleSheet.create({
|
||||||
previewMode: {
|
previewMode: {
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
...sharedStyles.textMedium
|
...sharedStyles.textMedium
|
||||||
|
},
|
||||||
|
searchbarContainer: {
|
||||||
|
marginBottom: 10,
|
||||||
|
paddingHorizontal: 15
|
||||||
|
},
|
||||||
|
reactionPickerSearchbar: {
|
||||||
|
paddingHorizontal: 20
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue