in projects/deliberation_at_scale/packages/frontend/components/ChatFlow/index.tsx [34:157]
export default function ChatFlow(props: Props) {
const { _ } = useLingui();
const defaultBotMessageTemplate: MessageTemplate = useMemo(() => {
return {
name: _(msg`AI Moderator`),
nameIcon: aiSolid,
highlighted: true,
} satisfies MessageTemplate;
}, [_]);
const { flow } = props;
const {
id: flowId,
steps: unfilteredSteps,
userMessageTemplate = defaultUserMessageTemplate,
botMessageTemplate = defaultBotMessageTemplate,
} = flow;
const steps = unfilteredSteps.filter((step) => {
return step.active ?? true;
});
const { push } = useLocalizedPush();
const searchParams = useSearchParams();
const params = useParams();
const mediaContext = useLocalMedia({ redirect: false, request: false });
const isVideoEnabled = mediaContext?.state?.isVideoEnabled ?? false;
const isAudioEnabled = mediaContext?.state?.isAudioEnabled ?? false;
const flowStateEntries = useAppSelector((state) => state.flow.flowStateLookup[flowId]);
const positionIndex = useAppSelector((state) => state.flow.flowPositionLookup[flowId] ?? 0);
const [lastHandledPositionIndex, setLastHandledPositionIndex] = useState<number>(-1);
const roomState = useAppSelector((state) => state.room);
const dispatch = useAppDispatch();
const { flowMessages } = useChatFlowMessages({
flowId,
});
const [inputDisabled, setInputDisabled] = useState(false);
const currentStep = steps?.[positionIndex] as (FlowStep | undefined);
const { hideInput = false, quickReplies = [], onTimeout } = currentStep ?? {};
const hasQuickReplies = !isEmpty(quickReplies);
const isTextInputDisabled = useMemo(() => {
const { onInput, hideInput } = currentStep ?? {};
return inputDisabled || !onInput || hideInput;
}, [inputDisabled, currentStep]);
const inputPlaceholder = useMemo(() => {
if (isTextInputDisabled) {
return hasQuickReplies ? _(msg`Select a button above...`) : _(msg`Waiting...`);
}
return undefined;
}, [hasQuickReplies, isTextInputDisabled, _]);
const { nickName } = useProfile();
const replaceMessageVariables = useCallback((text: string) => {
return replaceTextVariables(text, {
nickName,
});
}, [nickName]);
/** State helpers */
const reset = useCallback(() => {
dispatch(resetFlowPosition({
flowId,
}));
dispatch(resetFlowMessages({
flowId,
}));
}, [dispatch, flowId]);
const setFlowStateEntry = useCallback((key: string, value: any) => {
dispatch(setFlowStateEntryAction({
flowId,
key,
value,
}));
}, [dispatch, flowId]);
/* Navigation helpers */
const goTo = useCallback((deltaPosition: number) => {
dispatch(setFlowPosition({
flowId,
deltaPosition,
maxPosition: (flow.steps.length - 1) ?? 0,
}));
}, [dispatch, flow.steps.length, flowId]);
const goToNext = useCallback(() => {
goTo(1);
}, [goTo]);
const goToPrevious = useCallback(() => {
goTo(-1);
}, [goTo]);
const goToPage = useCallback((path: string) => {
push(path);
}, [push]);
/* This will advance to a subflow of choice */
const goToName = useCallback((name: string) => {
const index = flow.steps.findIndex((step) => {
return step?.name === name;
});
const deltaIndex = index - positionIndex;
goTo(deltaIndex);
}, [flow, goTo, positionIndex]);
/* This will add a message to the log */
const postMessages = useCallback((messages: Message[]) => {
// Append the messages to the array
dispatch(addFlowMessages({
flowId,
messages,
}));
}, [dispatch, flowId]);
const getMessageFromTemplate = useCallback((messagesOptions: MessagesOptions, messageTemplate: MessageTemplate) => {
const selectedMessages = draw(messagesOptions) ?? [];
const messages = selectedMessages.map((message) => {
const messageId = `${message}-${currentStep?.name ?? uuid()}}`;
return {
id: messageId,
...messageTemplate,
content: message,
date: dayjs().toISOString(),
} satisfies Message;
});
return messages;
}, [currentStep]);