Add useProps and getSelector (#166)
* Add useProps and getSelector getSelector get the right selector for component useProps merge 2 function calls together * Change name to usePropsFor Co-authored-by: Nan Jiang <jinan@microsoft.com>
This commit is contained in:
Родитель
183b26ff19
Коммит
dd07aaa851
|
@ -6,15 +6,12 @@ import {
|
|||
TypingIndicator,
|
||||
MessageThread,
|
||||
connectFuncsToContext,
|
||||
MapToErrorBarProps,
|
||||
useThreadId
|
||||
MapToErrorBarProps
|
||||
} from '@azure/communication-ui';
|
||||
import { useHandlers } from './hooks/useHandlers';
|
||||
import { chatThreadSelector, sendBoxSelector, typingIndicatorSelector } from '@azure/acs-chat-selector';
|
||||
import { Stack } from '@fluentui/react';
|
||||
import React, { useEffect, useMemo } from 'react';
|
||||
import { chatAreaContainerStyle, sendBoxParentStyle } from './styles/ChatArea.styles';
|
||||
import { useSelector } from './hooks/useSelector';
|
||||
import { usePropsFor } from './hooks/usePropsFor';
|
||||
|
||||
export interface ChatAreaProps {
|
||||
onRenderAvatar?: (userId: string) => JSX.Element;
|
||||
|
@ -28,39 +25,29 @@ export const ChatArea = (props: ChatAreaProps): JSX.Element => {
|
|||
// onRenderAvatar is a contoso callback. We need it to support emoji in Sample App. Sample App is currently on
|
||||
// components v0 so we're passing the callback at the component level. This might need further refactoring if this
|
||||
// ChatArea is to become a component or if Sample App is to move to composite
|
||||
const threadId = useThreadId();
|
||||
|
||||
const selectorConfig = useMemo(() => {
|
||||
return { threadId };
|
||||
}, [threadId]);
|
||||
const chatThreadProps = usePropsFor(MessageThread);
|
||||
const sendBoxProps = usePropsFor(SendBox);
|
||||
const typingIndicatorProps = usePropsFor(TypingIndicator);
|
||||
|
||||
const chatThreadProps = useSelector(chatThreadSelector, selectorConfig);
|
||||
const chatThreadHandlers = useHandlers(MessageThread);
|
||||
const sendBoxProps = useSelector(sendBoxSelector, selectorConfig);
|
||||
const sendBoxHandlers = useHandlers(SendBox);
|
||||
const typingIndicatorProps = useSelector(typingIndicatorSelector, selectorConfig);
|
||||
const onLoadPreviousChatMessages = chatThreadProps.onLoadPreviousChatMessages;
|
||||
|
||||
// Initialize the Chat thread with history messages
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
await chatThreadHandlers.onLoadPreviousChatMessages(5);
|
||||
await onLoadPreviousChatMessages(5);
|
||||
})();
|
||||
}, [chatThreadHandlers]);
|
||||
}, [onLoadPreviousChatMessages]);
|
||||
|
||||
return (
|
||||
<Stack className={chatAreaContainerStyle}>
|
||||
<MessageThread
|
||||
{...chatThreadProps}
|
||||
{...chatThreadHandlers}
|
||||
onRenderAvatar={props.onRenderAvatar}
|
||||
numberOfChatMessagesToReload={5}
|
||||
/>
|
||||
<MessageThread {...chatThreadProps} onRenderAvatar={props.onRenderAvatar} numberOfChatMessagesToReload={5} />
|
||||
<Stack.Item align="center" className={sendBoxParentStyle}>
|
||||
<div style={{ paddingLeft: '0.5rem', paddingRight: '0.5rem' }}>
|
||||
<TypingIndicator {...typingIndicatorProps} />
|
||||
</div>
|
||||
<ErrorBar />
|
||||
<SendBox {...sendBoxProps} {...sendBoxHandlers} />
|
||||
<SendBox {...sendBoxProps} />
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
);
|
||||
|
|
|
@ -63,9 +63,9 @@ export const ChatScreen = (props: ChatScreenProps): JSX.Element => {
|
|||
document.getElementById('sendbox')?.focus();
|
||||
}, []);
|
||||
|
||||
const chatHeaderProps = useSelector(chatHeaderSelector, { threadId: threadId });
|
||||
const chatHeaderProps = useSelector(chatHeaderSelector);
|
||||
const chatHeaderHandlers = useHandlers(ChatHeader);
|
||||
const chatParticipantProps = useSelector(chatParticipantListSelector, { threadId: threadId });
|
||||
const chatParticipantProps = useSelector(chatParticipantListSelector);
|
||||
|
||||
useEffect(() => {
|
||||
// We only want to check if we've fetched all the existing participants.
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
// © Microsoft Corporation. All rights reserved.
|
||||
|
||||
import { ChatClientState } from '@azure/acs-chat-declarative';
|
||||
import { chatThreadSelector, sendBoxSelector, typingIndicatorSelector } from '@azure/acs-chat-selector';
|
||||
import { MessageThread, SendBox, TypingIndicator } from '@azure/communication-ui';
|
||||
|
||||
import React from 'react';
|
||||
import { useHandlers } from './useHandlers';
|
||||
import { useSelector } from './useSelector';
|
||||
|
||||
type Selector = (state: ChatClientState, props: any) => any;
|
||||
export const usePropsFor = <SelectorT extends (state: ChatClientState, props: any) => any>(
|
||||
component: React.FunctionComponent<any>
|
||||
): ReturnType<SelectorT> => {
|
||||
const selector = getSelector(component);
|
||||
return { ...useSelector(selector), ...useHandlers(component) };
|
||||
};
|
||||
|
||||
export const getSelector = (component: React.FunctionComponent<any>): Selector => {
|
||||
switch (component) {
|
||||
case SendBox:
|
||||
return sendBoxSelector;
|
||||
case MessageThread:
|
||||
return chatThreadSelector;
|
||||
case TypingIndicator:
|
||||
return typingIndicatorSelector;
|
||||
}
|
||||
throw 'Can\'t find corresponding selector for this component. Please check the supported components from Azure Communication UI Feature Component List.';
|
||||
};
|
|
@ -1,23 +1,32 @@
|
|||
// © Microsoft Corporation. All rights reserved.
|
||||
|
||||
import { ChatClientState, DeclarativeChatClient } from '@azure/acs-chat-declarative';
|
||||
import { useChatClient } from '@azure/communication-ui';
|
||||
import { useChatClient, useThreadId } from '@azure/communication-ui';
|
||||
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
import { useState, useEffect, useRef, useMemo } from 'react';
|
||||
|
||||
// This function highly depends on chatClient.onChange event
|
||||
// It will be moved into selector folder when the ChatClientProvide when refactor finished
|
||||
export const useSelector = <SelectorT extends (state: ChatClientState, props: any) => any>(
|
||||
selector: SelectorT,
|
||||
selectorProps: Parameters<SelectorT>[1]
|
||||
selectorProps?: Parameters<SelectorT>[1]
|
||||
): ReturnType<SelectorT> => {
|
||||
const chatClient: DeclarativeChatClient = useChatClient() as any;
|
||||
const [props, setProps] = useState(selector(chatClient.state, selectorProps));
|
||||
const threadId = useThreadId();
|
||||
|
||||
const threadConfigProps = useMemo(
|
||||
() => ({
|
||||
threadId
|
||||
}),
|
||||
[threadId]
|
||||
);
|
||||
|
||||
const [props, setProps] = useState(selector(chatClient.state, selectorProps ?? threadConfigProps));
|
||||
const propRef = useRef(props);
|
||||
propRef.current = props;
|
||||
useEffect(() => {
|
||||
const onStateChange = (state: ChatClientState): void => {
|
||||
const newProps = selector(state, selectorProps);
|
||||
const newProps = selector(state, selectorProps ?? threadConfigProps);
|
||||
if (propRef.current !== newProps) {
|
||||
setProps(newProps);
|
||||
}
|
||||
|
@ -26,6 +35,6 @@ export const useSelector = <SelectorT extends (state: ChatClientState, props: an
|
|||
return () => {
|
||||
chatClient.offStateChange(onStateChange);
|
||||
};
|
||||
}, [chatClient, selector, selectorProps]);
|
||||
}, [chatClient, selector, selectorProps, threadConfigProps]);
|
||||
return props;
|
||||
};
|
||||
|
|
Загрузка…
Ссылка в новой задаче