Skip to content

Commit 6650109

Browse files
CRNS-74: KeyboardCompatibleView fixes
1 parent ec248a0 commit 6650109

File tree

3 files changed

+79
-22
lines changed

3 files changed

+79
-22
lines changed

src/components/Channel.js

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import PropTypes from 'prop-types';
99
// import { MessageSimple } from './MessageSimple';
1010
// import { Attachment } from './Attachment';
1111
import { ChannelInner } from './ChannelInner';
12+
import { KeyboardCompatibleView } from './KeyboardCompatibleView';
1213

1314
/**
1415
* Channel - Wrapper component for a channel. It needs to be place inside of the Chat component.
@@ -83,12 +84,42 @@ const Channel = withChatContext(
8384
* Override update message request (Advanced usage only)
8485
* */
8586
doUpdateMessageRequest: PropTypes.func,
87+
/**
88+
* If true, KeyboardCompatibleView wrapper is disabled.
89+
*
90+
* Channel component internally uses [KeyboardCompatibleView](https://github.com/GetStream/stream-chat-react-native/blob/master/src/components/KeyboardCompatibleView.js) component
91+
* internally to adjust the height of Channel component when keyboard is opened or dismissed. This prop gives you ability to disable this functionality, in case if you
92+
* want to use [KeyboardAvoidingView](https://facebook.github.io/react-native/docs/keyboardavoidingview) or you want to handle keyboard dismissal yourself.
93+
* KeyboardAvoidingView works well when your component occupies 100% of screen height, otherwise it may raise some issues.
94+
* */
95+
disableKeyboardCompatibleView: PropTypes.bool,
96+
/**
97+
* Custom wrapper component that handles height adjustment of Channel component when keyboard is opened or dismissed.
98+
* Defaults to [KeyboardCompatibleView](https://github.com/GetStream/stream-chat-react-native/blob/master/src/components/KeyboardCompatibleView.js)
99+
*
100+
* This prop can be used to configure default KeyboardCompatibleView component.
101+
* e.g.,
102+
* <Channel
103+
* channel={channel}
104+
* ...
105+
* KeyboardCompatibleView={(props) => {
106+
* return (
107+
* <KeyboardCompatibleView keyboardDismissAnimationDuration={200} keyboardOpenAnimationDuration={200}>
108+
* {props.children}
109+
* </KeyboardCompatibleView>
110+
* )
111+
* }}
112+
* />
113+
*/
114+
KeyboardCompatibleView: PropTypes.oneOfType([
115+
PropTypes.node,
116+
PropTypes.elementType,
117+
]),
86118
};
87119

88120
static defaultProps = {
89-
// LoadingIndicator,
90-
// Message: MessageSimple,
91-
// Attachment,
121+
disableKeyboardCompatibleView: false,
122+
KeyboardCompatibleView,
92123
};
93124

94125
render() {

src/components/ChannelInner.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import { emojiData } from '../utils';
1313
import { LoadingIndicator } from './LoadingIndicator';
1414
import { LoadingErrorIndicator } from './LoadingErrorIndicator';
1515
import { EmptyStateIndicator } from './EmptyStateIndicator';
16-
import { KeyboardCompatibleView } from './KeyboardCompatibleView';
1716
import { logChatPromiseExecution } from 'stream-chat';
1817

1918
/**
@@ -621,7 +620,7 @@ export class ChannelInner extends PureComponent {
621620

622621
render() {
623622
let core;
624-
623+
const { KeyboardCompatibleView } = this.props;
625624
if (this.state.error) {
626625
this.props.logger(
627626
'Channel component',
@@ -645,7 +644,9 @@ export class ChannelInner extends PureComponent {
645644
);
646645
} else {
647646
core = (
648-
<KeyboardCompatibleView>
647+
<KeyboardCompatibleView
648+
enabled={!this.props.disableKeyboardCompatibleView}
649+
>
649650
<ChannelContext.Provider value={this.getContext()}>
650651
<SuggestionsProvider
651652
handleKeyboardAvoidingViewEnabled={(trueOrFalse) => {

src/components/KeyboardCompatibleView.js

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
StatusBar,
99
} from 'react-native';
1010
import { KeyboardContext } from '../context';
11+
import PropTypes from 'prop-types';
1112

1213
/**
1314
* KeyboardCompatibleView is HOC component similar to [KeyboardAvoidingView](https://facebook.github.io/react-native/docs/keyboardavoidingview),
@@ -25,13 +26,40 @@ import { KeyboardContext } from '../context';
2526
* ```
2627
*/
2728
export class KeyboardCompatibleView extends React.PureComponent {
29+
static propTypes = {
30+
keyboardDismissAnimationDuration: PropTypes.number,
31+
keyboardOpenAnimationDuration: PropTypes.number,
32+
enabled: PropTypes.bool,
33+
};
34+
static defaultProps = {
35+
keyboardDismissAnimationDuration: 500,
36+
keyboardOpenAnimationDuration: 500,
37+
enabled: true,
38+
};
39+
2840
constructor(props) {
2941
super(props);
3042

3143
this.state = {
3244
channelHeight: new Animated.Value('100%'),
45+
// For some reason UI doesn't update sometimes, when state is updated using setValue.
46+
// So to force update the component, I am using following key, which will be increamented
47+
// for every keyboard slide up and down.
48+
key: 0,
3349
};
3450

51+
this.setupListeners();
52+
53+
this._keyboardOpen = false;
54+
// Following variable takes care of race condition between keyboardDidHide and keyboardDidShow.
55+
this._hidingKeyboardInProgress = false;
56+
this.rootChannelView = false;
57+
this.initialHeight = undefined;
58+
}
59+
60+
setupListeners = () => {
61+
if (!this.props.enabled) return;
62+
3563
if (Platform.OS === 'ios') {
3664
this.keyboardDidShowListener = Keyboard.addListener(
3765
'keyboardWillShow',
@@ -51,21 +79,15 @@ export class KeyboardCompatibleView extends React.PureComponent {
5179
'keyboardDidHide',
5280
this.keyboardDidHide,
5381
);
54-
55-
this._keyboardOpen = false;
56-
// Following variable takes care of race condition between keyboardDidHide and keyboardDidShow.
57-
this._hidingKeyboardInProgress = false;
58-
this.rootChannelView = false;
59-
this.initialHeight = undefined;
60-
}
61-
82+
};
6283
componentWillUnmount() {
6384
this.keyboardDidShowListener.remove();
6485
this.keyboardDidHideListener.remove();
6586
}
6687

6788
// TODO: Better to extract following functions to different HOC.
6889
keyboardDidShow = (e) => {
90+
if (!this.props.enabled) return;
6991
const keyboardHidingInProgressBeforeMeasure = this
7092
._hidingKeyboardInProgress;
7193
const keyboardHeight = e.endCoordinates.height;
@@ -79,9 +101,6 @@ export class KeyboardCompatibleView extends React.PureComponent {
79101
!keyboardHidingInProgressBeforeMeasure &&
80102
this._hidingKeyboardInProgress
81103
) {
82-
console.log(
83-
'Aborting keyboardDidShow operation since hide is in progress!',
84-
);
85104
return;
86105
}
87106

@@ -97,10 +116,13 @@ export class KeyboardCompatibleView extends React.PureComponent {
97116

98117
Animated.timing(this.state.channelHeight, {
99118
toValue: finalHeight,
100-
duration: 500,
119+
duration: this.props.keyboardOpenAnimationDuration,
101120
}).start(() => {
102121
// Force the final value, in case animation halted in between.
103122
this.state.channelHeight.setValue(finalHeight);
123+
this.setState({
124+
key: this.state.key + 1,
125+
});
104126
});
105127
});
106128
this._keyboardOpen = true;
@@ -110,12 +132,15 @@ export class KeyboardCompatibleView extends React.PureComponent {
110132
this._hidingKeyboardInProgress = true;
111133
Animated.timing(this.state.channelHeight, {
112134
toValue: this.initialHeight,
113-
duration: 500,
135+
duration: this.props.keyboardDismissAnimationDuration,
114136
}).start(() => {
115137
// Force the final value, in case animation halted in between.
116138
this.state.channelHeight.setValue(this.initialHeight);
117139
this._hidingKeyboardInProgress = false;
118140
this._keyboardOpen = false;
141+
this.setState({
142+
key: this.state.key + 1,
143+
});
119144
});
120145
};
121146

@@ -128,7 +153,7 @@ export class KeyboardCompatibleView extends React.PureComponent {
128153

129154
Animated.timing(this.state.channelHeight, {
130155
toValue: this.initialHeight,
131-
duration: 500,
156+
duration: this.props.keyboardDismissAnimationDuration,
132157
}).start((response) => {
133158
this.state.channelHeight.setValue(this.initialHeight);
134159
if (response && !response.finished) {
@@ -138,7 +163,7 @@ export class KeyboardCompatibleView extends React.PureComponent {
138163
// during keyboard dismissal.
139164
setTimeout(() => {
140165
resolve();
141-
}, 500);
166+
}, this.props.keyboardDismissAnimationDuration);
142167
return;
143168
}
144169

@@ -164,7 +189,7 @@ export class KeyboardCompatibleView extends React.PureComponent {
164189

165190
dismissKeyboard = async () => {
166191
Keyboard.dismiss();
167-
await this.keyboardWillDismiss();
192+
if (this.props.enabled) await this.keyboardWillDismiss();
168193
};
169194

170195
getContext = () => ({

0 commit comments

Comments
 (0)