react-native-live-markdown icon indicating copy to clipboard operation
react-native-live-markdown copied to clipboard

[feat]: Add option to pass list of usernames to be considered when mention user mode is activated

Open arpitv96171 opened this issue 1 year ago • 4 comments

When we type @ and mention user mode is activated, this arg value (which is an array of data) is used as a source to come as a list above/below where you are typing, to select from.

arpitv96171 avatar Sep 20 '24 09:09 arpitv96171

This will be possible once https://github.com/Expensify/react-native-live-markdown/pull/439 is merged.

tomekzaw avatar Sep 20 '24 10:09 tomekzaw

@tomekzaw I see that #439 is merged. is this functionality available now?

arpitv96171 avatar Jun 27 '25 09:06 arpitv96171

@arpitv96171 Yes, you should be able to customize parser logic now. I believe @Kicu can provide some more details on that.

tomekzaw avatar Jun 27 '25 09:06 tomekzaw

hey @arpitv96171 👋

We use react-native-live-markdown extensively in Expensify/App and I believe we're doing something similar to what you would like to achieve.

There are 3 parts to this:

  1. the new parser prop on MarkdownTextInput component - you need to create a worklet function and pass it with the parsing logic to MarkdownTextInput - if you want to use existing parsing logic, just import parseExpensiMark and use it. Take a look at this: https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/glossary#worklet
  2. you can use reanimated's shared value (https://docs.swmansion.com/react-native-reanimated/docs/core/useSharedValue) to send values to the worklet.
  3. when updating the shared value, you need to wrap it with runOnRuntime and use runtime provided by react-native-live-markdown here: https://github.com/Expensify/react-native-live-markdown/blob/main/src/MarkdownTextInput.tsx#L141 - this ensures that your values will actually be updated on the thread that live-markdown is running the parser.

Here's all this in action:

I'm defining a parser here: https://github.com/Expensify/App/blob/5c66742369ce506ef5ed51f1400040062000fd9f/src/components/RNMarkdownTextInput.tsx#L49-L59 in line 27 I'm defining the shared value for my list of available user mentions/logins:

const mentionsSharedVal = useSharedValue<string[]>(mentionsList)

Then the crucial part of the code is in lines 62-68

  useEffect(() => {
      runOnLiveMarkdownRuntime(() => {
          'worklet';

          mentionsSharedVal.set(mentionsList);
      })();
  }, [mentionsList, mentionsSharedVal]);

👆 any time my mentionsList changes, we update the sharedValue and the next time your parser runs, it will use the new data. You could probably just set the list once on init if it's static in your case.

Here is the definition of runOnLiveMarkdownRuntime:

import {getWorkletRuntime} from '@expensify/react-native-live-markdown';
import {runOnRuntime} from 'react-native-reanimated';

function runOnLiveMarkdownRuntime<Args extends unknown[], ReturnType>(worklet: (...args: Args) => ReturnType) {
    return runOnRuntime(getWorkletRuntime(), worklet);
}

We use this implementation only for mobile, since worklets on web do not exist. See details here: https://github.com/Expensify/App/tree/main/src/libs/runOnLiveMarkdownRuntime

Once you have the workletized parser then you can send whatever data you need to it via sharedValue and use this data when parsing.

Feel free to ask if anything is unclear 🙂

Kicu avatar Jun 27 '25 14:06 Kicu