1- import { type ComponentProps , useCallback , useEffect , useMemo , useState } from 'react' ;
1+ import {
2+ createContext ,
3+ useCallback ,
4+ useContext ,
5+ useEffect ,
6+ useMemo ,
7+ useState ,
8+ } from 'react' ;
29import { MessageComposer as MessageComposerController } from 'stream-chat' ;
310import type { LocalMessage , MessageComposerState } from 'stream-chat' ;
411import {
512 asDynamicKey ,
613 ContextMenuButton ,
7- defaultMessageActionSet ,
814 MessageUI as DefaultMessageUI ,
915 IconEdit ,
10- MessageActions ,
11- type MessageActionSetItem ,
1216 MessageComposer ,
1317 MessageComposerControllerProvider ,
1418 type MessageUIComponentProps ,
1519 useChatContext ,
16- useComponentContext ,
1720 useContextMenuContext ,
1821 useMessageContext ,
1922 useStateStore ,
2023 useThreadContext ,
2124 useTranslationContext ,
22- WithComponents ,
2325} from 'stream-chat-react' ;
2426
2527import { useAppSettingsSelector } from '../AppSettings' ;
2628import type { MessageActionSurface } from '../AppSettings' ;
2729
28- // Next to the built-in edit, so the two ways of editing read as alternatives.
29- const insertBeforeEdit = (
30- actionSet : MessageActionSetItem [ ] ,
31- actionSetItem : MessageActionSetItem ,
32- ) => {
33- const editIndex = actionSet . findIndex ( ( item ) => 'type' in item && item . type === 'edit' ) ;
30+ type InlineEditContextValue = {
31+ startEditing : ( message : LocalMessage ) => void ;
32+ } ;
3433
35- if ( editIndex < 0 ) return [ ...actionSet , actionSetItem ] ;
34+ // Lets the "Edit inline" action, rendered deep in the message's actions menu, start editing the
35+ // row it belongs to.
36+ const InlineEditContext = createContext < InlineEditContextValue | undefined > ( undefined ) ;
3637
37- return [ ...actionSet . slice ( 0 , editIndex ) , actionSetItem , ...actionSet . slice ( editIndex ) ] ;
38+ /**
39+ * The "Edit inline" message action. `ConfigurableMessageActions` adds it to the action set when
40+ * the setting is on; it only works inside an `InlineEditableMessage`, which provides the context.
41+ */
42+ export const InlineEditMessageAction = ( ) => {
43+ const inlineEdit = useContext ( InlineEditContext ) ;
44+ const { closeMenu } = useContextMenuContext ( ) ;
45+ const { message } = useMessageContext ( ) ;
46+ const { t } = useTranslationContext ( ) ;
47+
48+ if ( ! inlineEdit ) return null ;
49+
50+ return (
51+ < ContextMenuButton
52+ aria-label = { t (
53+ asDynamicKey ( 'viteExample.inlineEdit.action.ariaLabel' ) ,
54+ 'Edit message inline' ,
55+ ) }
56+ className = 'str-chat__message-actions-list-item-button'
57+ Icon = { IconEdit }
58+ onClick = { ( ) => {
59+ inlineEdit . startEditing ( message ) ;
60+ closeMenu ( ) ;
61+ } }
62+ >
63+ { t ( asDynamicKey ( 'viteExample.inlineEdit.action.label' ) , 'Edit inline' ) }
64+ </ ContextMenuButton >
65+ ) ;
3866} ;
3967
4068const editingSelector = ( state : MessageComposerState ) => ( {
@@ -55,7 +83,6 @@ export const InlineEditableMessage = (props: MessageUIComponentProps) => {
5583 ( state ) => state . messageActions ,
5684 ) ;
5785 const inlineEditEnabled = customMessageActions [ surface ] . inlineEdit ;
58- const { MessageActions : OuterMessageActions = MessageActions } = useComponentContext ( ) ;
5986
6087 // Only looks - a row nobody edits creates nothing. A composer found here holding an edit is one
6188 // left unfinished while this row was unmounted, so the editor comes straight back.
@@ -92,53 +119,7 @@ export const InlineEditableMessage = (props: MessageUIComponentProps) => {
92119 if ( ! inlineEditEnabled && editing ) editingComposer ?. clear ( ) ;
93120 } , [ editing , editingComposer , inlineEditEnabled ] ) ;
94121
95- const MessageActionsWithInlineEdit = useMemo ( ( ) => {
96- const InlineEditAction = ( ) => {
97- const { closeMenu } = useContextMenuContext ( ) ;
98- const { message } = useMessageContext ( ) ;
99- const { t } = useTranslationContext ( ) ;
100-
101- return (
102- < ContextMenuButton
103- aria-label = { t (
104- asDynamicKey ( 'viteExample.inlineEdit.action.ariaLabel' ) ,
105- 'Edit message inline' ,
106- ) }
107- className = 'str-chat__message-actions-list-item-button'
108- Icon = { IconEdit }
109- onClick = { ( ) => {
110- startEditing ( message ) ;
111- closeMenu ( ) ;
112- } }
113- >
114- { t ( asDynamicKey ( 'viteExample.inlineEdit.action.label' ) , 'Edit inline' ) }
115- </ ContextMenuButton >
116- ) ;
117- } ;
118-
119- const inlineEditActionSetItem : MessageActionSetItem = {
120- Component : InlineEditAction ,
121- placement : 'dropdown' ,
122- type : 'editInline' ,
123- } ;
124-
125- const Component = ( actionsProps : ComponentProps < typeof MessageActions > ) => {
126- const messageActionSet = useMemo (
127- ( ) =>
128- insertBeforeEdit (
129- actionsProps . messageActionSet ?? defaultMessageActionSet ,
130- inlineEditActionSetItem ,
131- ) ,
132- [ actionsProps . messageActionSet ] ,
133- ) ;
134-
135- return (
136- < OuterMessageActions { ...actionsProps } messageActionSet = { messageActionSet } />
137- ) ;
138- } ;
139- Component . displayName = 'MessageActionsWithInlineEdit' ;
140- return Component ;
141- } , [ OuterMessageActions , startEditing ] ) ;
122+ const inlineEditContextValue = useMemo ( ( ) => ( { startEditing } ) , [ startEditing ] ) ;
142123
143124 if ( ! inlineEditEnabled ) return < DefaultMessageUI { ...props } /> ;
144125
@@ -157,8 +138,8 @@ export const InlineEditableMessage = (props: MessageUIComponentProps) => {
157138 }
158139
159140 return (
160- < WithComponents overrides = { { MessageActions : MessageActionsWithInlineEdit } } >
141+ < InlineEditContext . Provider value = { inlineEditContextValue } >
161142 < DefaultMessageUI { ...props } />
162- </ WithComponents >
143+ </ InlineEditContext . Provider >
163144 ) ;
164145} ;
0 commit comments