Chatwoot Mobile App

repository·develop·Indexed 21 days ago

https://github.com/chatwoot/chatwoot-mobile-app

A React Native and Expo-based mobile application for the Chatwoot customer engagement platform. It enables agents to manage customer conversations, use canned responses, receive real-time notifications, and manage conversation statuses on iOS (13.4+) and Android (6.0+). Requires Chatwoot Server version 3.13.0 or higher.

Tokens
11.6K
Snippets
36
Records
50
Agent score
74%

What's inside chatwoot-mobile-app

  1. Overview of Chatwoot Mobile App

    develop

    The Chatwoot mobile app is a client for the Chatwoot platform, built using React Native and Expo. It allows agents to manage customer conversations, use canned responses, receive real-time notifications, communicate via private notes, and manage conversation statuses on the go.

    Compatibility Requirements

    • Chatwoot Server Version: 3.13.0 or higher
    • iOS: version 13.4+
    • Android: version 6.0+
  2. How to participate in Beta testing

    develop

    To test app updates before they are officially released, you can join the beta testing programs:

    Opting In

    Opting Out

    • Android: Visit the Android Beta link while logged into the Google Play email address used for opt-in, and click Leave the program.
    • iOS: Open the Chatwoot app page within the TestFlight app and select Stop Testing.
  3. Understand the AppNavigationContainer and linking configuration

    develop

    The AppNavigationContainer manages the NavigationContainer and implements complex deep-linking logic. It handles three primary scenarios:

    1. SSO Callbacks: Intercepts URLs containing SSO_CALLBACK_URL or auth/saml to process Single Sign-On authentication via SsoUtils without triggering standard navigation changes.
    2. Deep Links (Conversations): Parses URLs to navigate directly to a ChatScreen. The path pattern is app/accounts/:accountId/conversations/:conversationId/:primaryActorId?/:primaryActorType?.
    3. Push Notifications: When the app is opened via a notification (from a quit or background state), it uses findConversationLinkFromPush to resolve the notification payload into a valid deep link for the conversation.

    It also manages font loading (Inter family) and hides the SplashScreen once assets are ready.

  4. Manage multiple Swipeable rows with openedRowIndex

    develop

    To ensure that only one row is open at a time in a list, you must manage the openedRowIndex using a SharedValue from react-native-reanimated.

    When a user starts a pan gesture on a Swipeable component, it automatically updates the openedRowIndex.value to its own index. The component uses a useAnimatedReaction to monitor this value; if the openedRowIndex changes to a different index, the previously opened row will automatically trigger its closeRow logic to snap back to the closed state.

    Implementation Pattern:

    1. Create a useSharedValue<number | null>(null) in the parent list component.
    2. Pass this shared value to every Swipeable instance in the list.
    3. Pass the unique index of each item to the corresponding Swipeable instance.
  5. Configure App entrypoint and environment behaviors

    develop

    The App.tsx file serves as the main entrypoint for the Chatwoot mobile application. It handles environment-specific configurations including Sentry error tracking, Reactotron for development debugging, and Storybook for UI component development.

    Environment Behaviors

    • Production (!__DEV__): Initializes Sentry for error monitoring and wraps the main App component with Sentry.wrap to capture errors and traces. It requires the EXPO_PUBLIC_SENTRY_DSN environment variable.
    • Development (__DEV__): Loads ReactotronConfig for debugging and returns the standard App component.
    • Storybook Mode: If the Expo configuration extra.eas.storybookEnabled is set to 'true', the application loads the Storybook entrypoint instead of the main application.

    Required Environment Variables

    • EXPO_PUBLIC_SENTRY_DSN: The DSN for Sentry error reporting.
    // Sentry configuration used in production
    if (!__DEV__) {
      Sentry.init({
        dsn: process.env.EXPO_PUBLIC_SENTRY_DSN,
        tracesSampleRate: 1.0,
        attachScreenshot: true,
      });
    }
  6. Configure Metro with Sentry and Storybook

    develop

    The project uses a customized Metro configuration that integrates Sentry for error tracking and Storybook for UI component development. The configuration follows this precedence:

    1. Starts with the default Expo Metro configuration.
    2. Merges in Sentry Expo configuration via getSentryExpoConfig.
    3. Wraps the resulting configuration with withStorybook to enable Storybook support.

    To enable Storybook, the withStorybook wrapper requires an options object specifying the enabled state and the configPath pointing to the .storybook directory.

    const config = {
      ...defaultConfig,
      ...sentryConfig,
    };
    
    module.exports = withStorybook(config, {
      enabled: true,
      configPath: path.resolve(__dirname, './.storybook'),
    });
  7. Configure Chatwoot Mobile App via app.config.ts

    develop

    The project uses an app.config.ts file (Expo configuration) to manage app metadata, platform-specific settings, and plugin configurations. Many settings are driven by environment variables prefixed with EXPO_PUBLIC_.

    Key Environment Variables

    • EXPO_PUBLIC_APP_SLUG: The slug for the app (defaults to chatwoot-mobile).
    • EXPO_PUBLIC_IOS_GOOGLE_SERVICES_FILE: Relative path to the iOS Google Services file.
    • EXPO_PUBLIC_ANDROID_GOOGLE_SERVICES_FILE: Relative path to the Android Google Services file.
    • EXPO_PUBLIC_PROJECT_ID: EAS project ID.
    • EXPO_PUBLIC_SENTRY_PROJECT_NAME: Sentry project name.
    • EXPO_PUBLIC_SENTRY_ORG_NAME: Sentry organization name.
    • EXPO_STORYBOOK_ENABLED: Flag to enable Storybook.
  8. Configure React Native autolinking overrides

    develop

    The react-native.config.js file is used to customize how React Native autolinks native modules. In this project, specific dependencies are configured to prevent automatic linking on the Android platform by setting the android platform key to null within the dependencies object. This is typically done to manage manual linking requirements or to resolve conflicts with specific native libraries.

    module.exports = {
      dependencies: {
        'ffmpeg-kit-react-native': {
          platforms: {
            android: null, // 👈 prevents Android autolinking
          },
        },
        '@notifee/react-native': {
          platforms: {
            android: null, // 👈 prevents Android autolinking
          },
        },
      },
    };
  9. Use the AudioRecorder component

    develop

    The AudioRecorder component provides a UI for recording audio messages within a chat interface. It handles permission requests (on Android), recording lifecycle (start, pause, resume, stop), and file format conversion.

    Props

    PropTypeDescription
    onRecordingComplete(audioFile: File) => voidCallback triggered when the recording is successfully stopped and processed. The audioFile object contains the URI, name, and file size.
    audioFormat'audio/m4a' | 'audio/wav'The desired output format. If 'audio/wav' is selected, the component automatically converts the internal AAC recording to WAV using convertAacToWav.

    Behavior

    • Android Permissions: Automatically requests PermissionsAndroid.PERMISSIONS.RECORD_AUDIO upon mounting.
    • File Storage: On Android, files are stored in the CacheDir. On iOS, they are stored with a unique identifier.
    • State Management: When a recording is completed, the component dispatches the new file path to the localRecordedAudioCacheSlice via addNewCachePath to ensure the file is tracked in the local cache.
    import { AudioRecorder } from './components/audio-recorder/AudioRecorder';
    
    // Usage example
    <AudioRecorder
      audioFormat="audio/wav"
      onRecordingComplete={(audioFile) => {
        console.log('Recorded file:', audioFile.uri);
        // Handle the file (e.g., upload to server)
      }}
    />
  10. Validate file size and update attachments

    develop

    Use validateFileAndSetAttachments to check if a selected file exceeds the MAXIMUM_FILE_UPLOAD_SIZE before adding it to the conversation.

    • If the file is within the limit, it dispatches updateAttachments([attachment]) to the Redux store.
    • If the file exceeds the limit, it shows a toast notification with the error message from CONVERSATION.FILE_SIZE_LIMIT.

    Parameters:

    • dispatch: The Redux dispatch function.
    • attachment: An object containing at least a fileSize property.
    import { validateFileAndSetAttachments } from '@/screens/chat-screen/components/message-components/CommandOptionsMenu';
    
    // Usage
    validateFileAndSetAttachments(dispatch, selectedAsset);
  11. Handle photo library access

    develop

    Use handleOpenPhotosLibrary to launch the device's image library. It allows selecting up to 4 mixed media assets. If permissions are denied, it triggers an alert with an option to open device settings via Linking.openSettings(). Successful selections are passed to validateFileAndSetAttachments to be added to the conversation.

    Note: This function requires a dispatch function as an argument to update the Redux store.

    import { handleOpenPhotosLibrary } from '@/screens/chat-screen/components/message-components/CommandOptionsMenu';
    
    // Usage within a component
    const handlePress = async () => {
      await handleOpenPhotosLibrary(dispatch);
    };