stream-chat-react-native icon indicating copy to clipboard operation
stream-chat-react-native copied to clipboard

[🐛] The InputBox and SendButton goes wrong when users copy and paste multiple times in the textarea

Open atsss opened this issue 2 years ago • 2 comments

Issue

The send button doesn't activate when users copy and paste multiple times. The textarea also should expand depending on the number of lines. However, it doesn't change either. Once the user starts editing in the situation, the send button is activated and the textarea is expanded

Steps to reproduce

Steps to reproduce the behavior:

  1. Go to any channels
  2. Copy and paste a previous message in textarea
  3. Send it to the chat
  4. Copy and paste a previous message in textarea again

Expected behavior

InputBox and SendButton should work properly regardless to the number of copying and pasting

Project Related Information

Customization

Click To Expand

  • Create sample app using it as a reference
import React, { useContext, useEffect, useMemo, useState } from 'react';
import { I18nManager, LogBox, Platform, SafeAreaView, useColorScheme, View } from 'react-native';
import { DarkTheme, DefaultTheme, NavigationContainer, RouteProp } from '@react-navigation/native';
import { createStackNavigator, StackNavigationProp } from '@react-navigation/stack';
import { useHeaderHeight } from '@react-navigation/elements';
import { SafeAreaProvider, useSafeAreaInsets } from 'react-native-safe-area-context';
import { Channel as ChannelType, ChannelSort, StreamChat } from 'stream-chat';
import {
  Channel,
  ChannelList,
  Chat,
  MessageInput,
  MessageList,
  OverlayProvider,
  Streami18n,
  Thread,
  ThreadContextValue,
  useAttachmentPickerContext,
  useOverlayContext,
} from 'stream-chat-expo';
import { GestureHandlerRootView } from 'react-native-gesture-handler';

import { useStreamChatTheme } from './src/hooks/useStreamChatTheme';

LogBox.ignoreAllLogs(true);

type LocalAttachmentType = Record<string, unknown>;
type LocalChannelType = Record<string, unknown>;
type LocalCommandType = string;
type LocalEventType = Record<string, unknown>;
type LocalMessageType = Record<string, unknown>;
type LocalReactionType = Record<string, unknown>;
type LocalUserType = Record<string, unknown>;

type StreamChatGenerics = {
  attachmentType: LocalAttachmentType;
  channelType: LocalChannelType;
  commandType: LocalCommandType;
  eventType: LocalEventType;
  messageType: LocalMessageType;
  reactionType: LocalReactionType;
  userType: LocalUserType;
};

const options = {
  presence: true,
  state: true,
  watch: true,
  limit: 30,
};

I18nManager.forceRTL(false);

const chatClient = StreamChat.getInstance<StreamChatGenerics>('MY_APP_ID');
const userToken = 'MY_USER_TOKEN';

const user = {
  id: 'MY_USER_ID',
};
const filters = {
  members: { $in: ['MY_USER_ID''] },
  type: 'Coaching',
};
const sort: ChannelSort<StreamChatGenerics> = { last_updated: -1 };

/**
 * Start playing with streami18n instance here:
 * Please refer to description of this PR for details: https://github.com/GetStream/stream-chat-react-native/pull/150
 */
const streami18n = new Streami18n({
  language: 'en',
});

type ChannelListScreenProps = {
  navigation: StackNavigationProp<NavigationParamsList, 'ChannelList'>;
};

const ChannelListScreen: React.FC<ChannelListScreenProps> = ({ navigation }) => {
  const { setChannel } = useContext(AppContext);

  const memoizedFilters = useMemo(() => filters, []);

  return (
    <View style={{ height: '100%' }}>
      <ChannelList<StreamChatGenerics>
        filters={memoizedFilters}
        onSelect={(channel) => {
          setChannel(channel);
          navigation.navigate('Channel');
        }}
        options={options}
        sort={sort}
      />
    </View>
  );
};

type ChannelScreenProps = {
  navigation: StackNavigationProp<NavigationParamsList, 'Channel'>;
};

const ChannelScreen: React.FC<ChannelScreenProps> = ({ navigation }) => {
  const { channel, setThread, thread } = useContext(AppContext);
  const headerHeight = useHeaderHeight();
  const { setTopInset } = useAttachmentPickerContext();
  const { overlay } = useOverlayContext();

  useEffect(() => {
    navigation.setOptions({
      gestureEnabled: Platform.OS === 'ios' && overlay === 'none',
    });
  }, [overlay]);

  useEffect(() => {
    setTopInset(headerHeight);
  }, [headerHeight]);

  if (channel === undefined) {
    return null;
  }

  return (
    <SafeAreaView>
      <Channel channel={channel} keyboardVerticalOffset={headerHeight} thread={thread}>
        <View style={{ flex: 1 }}>
          <MessageList<StreamChatGenerics>
            onThreadSelect={(thread) => {
              setThread(thread);
              if (channel?.id) {
                navigation.navigate('Thread');
              }
            }}
          />
          <MessageInput />
        </View>
      </Channel>
    </SafeAreaView>
  );
};

type ThreadScreenProps = {
  navigation: StackNavigationProp<ThreadRoute, 'Thread'>;
  route: RouteProp<ThreadRoute, 'Thread'>;
};

const ThreadScreen: React.FC<ThreadScreenProps> = ({ navigation }) => {
  const { channel, setThread, thread } = useContext(AppContext);
  const headerHeight = useHeaderHeight();
  const { overlay } = useOverlayContext();

  useEffect(() => {
    navigation.setOptions({
      gestureEnabled: Platform.OS === 'ios' && overlay === 'none',
    });
  }, [overlay]);

  if (!channel) return null;

  return (
    <SafeAreaView>
      <Channel channel={channel} keyboardVerticalOffset={headerHeight} thread={thread} threadList>
        <View
          style={{
            flex: 1,
            justifyContent: 'flex-start',
          }}
        >
          <Thread<StreamChatGenerics> onThreadDismount={() => setThread(null)} />
        </View>
      </Channel>
    </SafeAreaView>
  );
};

type ChannelRoute = { Channel: undefined };
type ChannelListRoute = { ChannelList: undefined };
type ThreadRoute = { Thread: undefined };
type NavigationParamsList = ChannelRoute & ChannelListRoute & ThreadRoute;

const Stack = createStackNavigator<NavigationParamsList>();

type AppContextType = {
  channel: ChannelType<StreamChatGenerics> | undefined;
  setChannel: React.Dispatch<React.SetStateAction<ChannelType<StreamChatGenerics> | undefined>>;
  setThread: React.Dispatch<
    React.SetStateAction<ThreadContextValue<StreamChatGenerics>['thread'] | undefined>
  >;
  thread: ThreadContextValue<StreamChatGenerics>['thread'] | undefined;
};

const AppContext = React.createContext({} as AppContextType);

const App = () => {
  const colorScheme = useColorScheme();
  const { bottom } = useSafeAreaInsets();
  const theme = useStreamChatTheme();

  const [channel, setChannel] = useState<ChannelType<StreamChatGenerics>>();
  const [clientReady, setClientReady] = useState(false);
  const [thread, setThread] = useState<ThreadContextValue<StreamChatGenerics>['thread']>();

  useEffect(() => {
    const setupClient = async () => {
      const connectPromise = chatClient.connectUser(user, userToken);
      setClientReady(true);
      await connectPromise;
    };

    setupClient();
  }, []);

  return (
      <NavigationContainer
        theme={{
          colors: {
            ...(colorScheme === 'dark' ? DarkTheme : DefaultTheme).colors,
            background: theme.colors?.white_snow || '#FCFCFC',
          },
          dark: colorScheme === 'dark',
        }}
      >
        <AppContext.Provider value={{ channel, setChannel, setThread, thread }}>
          <GestureHandlerRootView style={{ flex: 1 }}>
            <OverlayProvider<StreamChatGenerics>
              bottomInset={bottom}
              i18nInstance={streami18n}
              value={{ style: theme }}
            >
              <Chat client={chatClient} i18nInstance={streami18n}>
                {clientReady && (
                  <Stack.Navigator
                    initialRouteName='ChannelList'
                    screenOptions={{
                      headerTitleStyle: { alignSelf: 'center', fontWeight: 'bold' },
                    }}
                  >
                    <Stack.Screen
                      component={ChannelScreen}
                      name='Channel'
                      options={() => ({
                        headerBackTitle: 'Back',
                        headerRight: () => <></>,
                        headerTitle: channel?.data?.name,
                      })}
                    />
                    <Stack.Screen
                      component={ChannelListScreen}
                      name='ChannelList'
                      options={{ headerTitle: 'Channel List' }}
                    />
                    <Stack.Screen
                      component={ThreadScreen}
                      name='Thread'
                      options={() => ({ headerBackTitle: 'back' })}
                    />
                  </Stack.Navigator>
                )}
              </Chat>
            </OverlayProvider>
          </GestureHandlerRootView>
        </AppContext.Provider>
      </NavigationContainer>
  );
};

export default () => {
  const theme = useStreamChatTheme();

  return (
    <SafeAreaProvider style={{ backgroundColor: theme.colors?.white_snow || '#FCFCFC' }}>
      <App />
    </SafeAreaProvider>
  );
};

Offline support

  • [ ] I have enabled offline support.
  • [x] The feature I'm having does not occur when offline support is disabled. (stripe out if not applicable)

Environment

Click To Expand

package.json:

  "dependencies": {
    "@expo/vector-icons": "^13.0.0",
    "@react-navigation/elements": "^1.3.3",
    "@react-navigation/native": "^6.0.10",
    "@react-navigation/stack": "^6.2.1",
    "expo": "^48.0.0",
    "expo-application": "~5.1.1",
    "expo-av": "~13.2.1",
    "expo-clipboard": "~4.1.2",
    "expo-constants": "~14.2.1",
    "expo-dev-client": "~2.2.1",
    "expo-document-picker": "~11.2.2",
    "expo-file-system": "~15.2.2",
    "expo-haptics": "~12.2.1",
    "expo-image-manipulator": "~11.1.1",
    "expo-image-picker": "~14.1.1",
    "expo-keep-awake": "~12.0.1",
    "expo-linking": "~4.0.1",
    "expo-localization": "~14.1.1",
    "expo-location": "~15.1.1",
    "expo-media-library": "~15.2.3",
    "expo-sharing": "~11.2.2",
    "expo-splash-screen": "~0.18.2",
    "expo-status-bar": "~1.4.4",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "react-native": "0.71.8",
    "react-native-safe-area-context": "4.5.0",
    "react-native-screens": "~3.20.0",
    "react-native-storage": "^1.0.1",
    "react-native-svg": "13.4.0",
    "react-native-web": "~0.18.7",
    "stream-chat-expo": "5.22.1",
  },

react-native info output:

 OUTPUT GOES HERE
  • Platform that you're experiencing the issue on:
    • [x] iOS
    • [ ] Android
    • [ ] iOS but have not tested behavior on Android
    • [ ] Android but have not tested behavior on iOS
    • [ ] Both
  • stream-chat-react-native version you're using that has this issue:
    • "stream-chat-expo": "5.22.1"
  • Device/Emulator info:
    • [x] I am using a physical device
    • OS version: iOS 16.1.1 , Android13, Android14 , Android15
    • Device/Emulator: iPhone 12 , Pixel 3a , Pixel 4 , Pixel6

Additional context

Screenshots

Click To Expand

Feb-09-2024 12-18-18


atsss avatar Feb 09 '24 12:02 atsss

Hey @atsss, this isn't reproducible for us on the iPhone simulator as the one that you have shared in the video.

Ref:

https://github.com/GetStream/stream-chat-react-native/assets/39884168/1e85e04a-f286-419f-bbbe-977569100191

khushal87 avatar Feb 12 '24 05:02 khushal87

@khushal87 Thanks for your response. It's happening to me both simulators and real devices. Can you share your code which doesn't have problems and the simulator/OS version? I'm having this problem with the following sample code. I just copied and pasted the sample and changed app credentials.

https://github.com/GetStream/stream-chat-react-native/blob/4250706d374e06267e161b410bd8d1b2ef0b4896/examples/TypeScriptMessaging/App.tsx

atsss avatar Feb 14 '24 12:02 atsss

Closing this issue because of not being able to reproduce it. Please feel free to open it if you see it's still relevant. Test it on our example apps as well.

khushal87 avatar Apr 22 '24 17:04 khushal87