FaridSafi / react-native-gifted-chat

đź’¬ The most complete chat UI for React Native
https://gifted.chat
MIT License
13.5k stars 3.55k forks source link

Input Accessory And Keyboard Behavior Like Messenger App #2242

Open 000xuandu opened 2 years ago

000xuandu commented 2 years ago

Hi guys!

CAN BE DONE?

Can GiftedChat do the same input/keyboard effect as the Messenger app?

The keyboard will be dismiss when user click Gif/Image icon in the accessory, and will come back when user focus text input!

I tried to research about this, and I found: KeyboardAccessoryView component, develop by Wix. But it has problem in Android, I created an issue here, haven’t fixed yet: https://github.com/wix/react-native-ui-lib/issues/2063

https://user-images.githubusercontent.com/33916400/179034560-04211fa3-c1a5-4ea8-b936-147113acbc73.MOV

fukemy commented 2 years ago

Same problem

huuthinh95 commented 2 years ago

You can try lib: https://github.com/thomasgazzoni/react-native-keyboard-area for android. You change the SoftInput mode to ADJUST_NOTHING only for the pages, example:

import { RNKeyboard, SoftInputMode } from 'react-native-keyboard-area';

componentDidMount() {
  navigation.addListener('blur', this.componentDidExit);
  navigation.addListener('focus', this.componentDidEnter);
}

componentDidEnter = () => {
  if (Platform.OS === 'android') {
    RNKeyboard.setWindowSoftInputMode(SoftInputMode.SOFT_INPUT_ADJUST_NOTHING,);
  }
};

componentDidExit = () => {
  if (Platform.OS === 'android') {
    RNKeyboard.setWindowSoftInputMode(SoftInputMode.SOFT_INPUT_ADJUST_RESIZE);
  }
};

componentWillUnmount() {
  navigation.removeListener('blur', this.componentDidExit);
  navigation.removeListener('focus', this.componentDidEnter);
}
useEffect(() => {
    if (Platform.OS === 'ios') {
      const showSubscription = Keyboard.addListener('keyboardWillShow', (e) => {
        keyboardheight = e.endCoordinates.height;
      });
      const hideSubscription = Keyboard.addListener('keyboardWillHide', (e) => {
        keyboardheight = 0;
      });
      return () => {
        showSubscription.remove();
        hideSubscription.remove();
      };
    }
  }, []);
fukemy commented 2 years ago

hi @huuthinh95 , can u help my problem? I can not switch keyboard sticker Here is my main view

<View style={{
                position: 'absolute',
                width: '100%',
                height: '100%',
            }}>
                {renderGiftedChat()}
                <KeyboardSpacer
                    ref={kbRef}
                    isOpen={customKeyboard == KEYBOARD_SYSTEM}
                    onChange={(isOpen: boolean, currentHeight: number) => {
                        console.log('onchange', isOpen, currentHeight)
                    }}>   

                    {renderAccessory()}
                </KeyboardSpacer>
            </View>

  const renderAccessory = (props) => {
        if (customKeyboard == KEYBOARD_EMOJI) return <ChatEmojiKeyboardInput width={width} height={getKbHeight} onSelectEmoji={onSelectEmoji} />
        if (customKeyboard == KEYBOARD_MORE) return <ChatMoreKeyboardInput width={width} height={getKbHeight} onSelectedFile={onSelectedFile} onShareContact={onShareContact}/>
        if (customKeyboard == KEYBOARD_VOICE) return <ChatVoiceKeyboardInput width={width} height={getKbHeight} onSendVoice={onSendVoice} />
        return null
    }

Can u tell me what's wrong with my code? Thanks so much

huuthinh95 commented 2 years ago

hi @fukemy Your problem on android or IOS. If on android, you need set SoftInput mode to _ADJUSTNOTHING.

import { RNKeyboard, SoftInputMode } from 'react-native-keyboard-area';

componentDidMount() {
  navigation.addListener('blur', this.componentDidExit);
  navigation.addListener('focus', this.componentDidEnter);
}

componentDidEnter = () => {
  if (Platform.OS === 'android') {
    RNKeyboard.setWindowSoftInputMode(SoftInputMode.SOFT_INPUT_ADJUST_NOTHING,);
  }
};

componentDidExit = () => {
  if (Platform.OS === 'android') {
    RNKeyboard.setWindowSoftInputMode(SoftInputMode.SOFT_INPUT_ADJUST_RESIZE);
  }
};

componentWillUnmount() {
  navigation.removeListener('blur', this.componentDidExit);
  navigation.removeListener('focus', this.componentDidEnter);
}

And you can try your code on IOS.

fukemy commented 2 years ago

ya, it's work as expected, im using KeyboardArea it's almost do the job for me. Thanks

000xuandu commented 2 years ago

@huuthinh95 Thank you! The iOS is smooth but Android still flickers

huuthinh95 commented 2 years ago

@000xuandu you can try this code.

import React, { useState, useCallback, useEffect, useRef } from 'react';
import { TextInput, TouchableOpacity, View, Text, Keyboard, Animated, Platform } from 'react-native';
import { GiftedChat } from 'react-native-gifted-chat';
import { RNKeyboard, SoftInputMode, KeyboardArea, KeyboardAreaRef } from 'react-native-keyboard-area';

export function App() {
  const [messages, setMessages] = useState([]);
  const heightAni = useRef(new Animated.Value(0)).current;
  const flag = useRef(false);
  const componentDidEnter = () => {
    if (Platform.OS === 'android') {
      RNKeyboard.setWindowSoftInputMode(SoftInputMode.SOFT_INPUT_ADJUST_NOTHING,);
    }
  };

  const componentDidExit = () => {
    if (Platform.OS === 'android') {
      RNKeyboard.setWindowSoftInputMode(SoftInputMode.SOFT_INPUT_ADJUST_RESIZE);
    }
  };

  useEffect(() => {
    RNKeyboard.addKeyboardListener(keyboardAreaHeightChanged);
    return () => {
      RNKeyboard.removeKeyboardListener(keyboardAreaHeightChanged)
    }
  }, [])

  const keyboardAreaHeightChanged = useCallback((currentHeight) => {
    // Your logic
    if (flag.current) {
      flag.current = false;
      return;
    }
    Animated.timing(heightAni, {
      toValue: currentHeight,
      duration: 0,
      useNativeDriver: false
    }).start();
  }, []);

  useEffect(() => {
    componentDidEnter()
    return () => {
      componentDidExit()
    };
  }, [])

  useEffect(() => {
    setMessages([
      {
        _id: 1,
        text: 'Hello developer',
        createdAt: new Date(),
        user: {
          _id: 2,
          name: 'React Native',
          avatar: 'https://placeimg.com/140/140/any',
        },
      },
    ])
  }, []);
  const onSend = useCallback((messages = []) => {
    setMessages(previousMessages => GiftedChat.append(previousMessages, messages))
  }, []);

  const renderInputToolbar = () => {
    return (
      <Animated.View style={{borderWidth: 1, borderColor: 'red', flex: 1}}>
        <TextInput style={{borderWidth: 1, borderColor: 'blue'}} />
        <TouchableOpacity onPress={handleChangeMode}>
          <Text>Test</Text>
        </TouchableOpacity>
      </Animated.View>
    )
  }

  const handleChangeMode = () => {
    if (heightAni._value === 0) {
      Animated.timing(heightAni, {
        toValue: 305,//get height of keyboard
        duration: 100,
        useNativeDriver: false
      }).start();
    } else {
      flag.current = true'''
      Keyboard.dismiss()
    }
  }

  return (
    <View style={{flex: 1}}>
      <GiftedChat
        messages={messages}
        onSend={messages => onSend(messages)}
        user={{
          _id: 1,
        }}
        multiline
        renderComposer={renderInputToolbar}
      />
      {/* <KeyboardArea
        ref={keyboardSpacerRef.current}
        isOpen={false}
        onChange={keyboardAreaHeightChanged}
      >
      </KeyboardArea> */}
      <Animated.View style={{height: heightAni }}>
        <Text>custom content</Text>
      </Animated.View>
    </View>
  )
}

export default App;
fukemy commented 2 years ago

hi @huuthinh95 , i want to make the view below keyboard look like bottom sheet, such as the sticker keyboard of Messenger, did u faced this?

OPEN-9 commented 2 years ago

@huuthinh95 Android reported this error. Do you have any better suggestions? Thank you

image

fukemy commented 2 years ago

did u used expo?