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

feat: new MessageActions component

Open arnautov-anton opened this issue 1 year ago • 1 comments

The Problem(s)

MessageActions/MessageOptions/MessageActionsBox are a poorly designed set of components which we were trying to patch over the time with things like CustomMessageActionsList but with each new patchy addition, the component set got heavier and worse to navigate in when it came to customization.

  • messageActions prop on MessageList and VirtualizedMessageList components allow for key/handler customization:
    { "Custom Action": () => doThing() }
    
    Solution like this falls face first when integrators need to access MessageContext or use translations for different languages (the key is used as button text).
  • Solution to patch those shortcomings was to introduce CustomMessageActionsList which allows to render custom buttons within actions dropdown but... when buttons are rendered conditionally, the "..." is still rendered and upon clicking on it, it opens up an empty actions dropdown. This solution also does not allow to adjust "quick actions" (like reply or react).

Steps Taken

  1. pre-define default set of actions with buttons as components with own logic and click handlers, separate into "quick" and "dropdown" types
  2. define default filter which takes care of user capabilities and whether the actions are allowed within reply type of a message (and some other stuff based on message type or status)
  3. allow integrators to override the default set and to override the default filter function, alow reusing defaults too
access action set -> filter action set based on filter function criteria -> separate into quick and dropdown -> render

From Integrator's POV

import { Channel, MessageActions_UNSTABLE } from 'stream-chat-react';
const {
  MessageActions,
  defaultMessageActionSet,
  DefaultDropdownActionButton,
} = MessageActions_UNSTABLE;

const CustomMessageActions = () => {
  const customFilter = () => {
    /*...*/
  };

  return (
    <MessageActions
      // though not recommended, it's completely possible to disable default filter...
      disableBaseMessageActionSetFilter
      messageActionSet={[
        ...defaultMessageActionSet,
        {
          type: 'myCustomTypeDropdown',
          placement: 'dropdown',
          // we can enforce non-null return type (at least through TS)
          Component: () => <DefaultDropdownActionButton>🚀 Custom</DefaultDropdownActionButton>,
        },
        {
          type: 'myCustomTypeQuick',
          placement: 'quick',
          Component: () => <button>a</button>,
        },
        // ...and apply custom filter here with access to CustomMessageActions scope (contexts + other hooks)
      ].filter(customFilter)}
    />
  );
};

<Channel MessageOptions={CustomMessageActions}>...</Channel>;

image

Next Steps

  • [ ] review and beta release
  • [ ] allow overriding certain components within default MessageActions_UNSTABLE
  • [ ] tests

arnautov-anton avatar Oct 24 '24 22:10 arnautov-anton