* on click focus on activity body editor * acitivity editor hot key scope added * classname prop added escape hot key scope call back added * passing containerClassName prop for activity editor * hot key scope added * console log cleanup * activity target escape hot key listener added * tasks filter hot key scope refactor * scope renaming refactor * imports order linting refactor * imports order linting refactor * acitivity editor field focus state and body editor text listener added * logic refactor removed state for activity editor fields focus * removed conflicting click handler of inline cell creating new scope * linting and formatting * acitivity editor field focus state and body editor text listener added * adding text at the end of line * fix duplicate imports * styling: gap fix activity editor * format fix * Added comments * Fixes * Remove useListenClickOutside, state, onFocus and onBlur * Keep simplifying * Complete review * Fix lint --------- Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com> Co-authored-by: Charles Bochet <charles@twenty.com>
131 lines
3.8 KiB
TypeScript
131 lines
3.8 KiB
TypeScript
import styled from '@emotion/styled';
|
|
import { isNonEmptyString } from '@sniptt/guards';
|
|
import { useRecoilValue } from 'recoil';
|
|
import { v4 } from 'uuid';
|
|
|
|
import { Comment } from '@/activities/comment/Comment';
|
|
import { Activity } from '@/activities/types/Activity';
|
|
import { Comment as CommentType } from '@/activities/types/Comment';
|
|
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
|
import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
|
|
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
|
|
import {
|
|
AutosizeTextInput,
|
|
AutosizeTextInputVariant,
|
|
} from '@/ui/input/components/AutosizeTextInput';
|
|
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
|
|
|
const StyledThreadItemListContainer = styled.div`
|
|
align-items: flex-start;
|
|
border-top: 1px solid ${({ theme }) => theme.border.color.light};
|
|
|
|
box-sizing: border-box;
|
|
display: flex;
|
|
flex-direction: column;
|
|
|
|
gap: ${({ theme }) => theme.spacing(4)};
|
|
|
|
justify-content: flex-start;
|
|
padding: ${({ theme }) => theme.spacing(8)};
|
|
padding-left: ${({ theme }) => theme.spacing(12)};
|
|
width: 100%;
|
|
`;
|
|
|
|
const StyledCommentActionBar = styled.div`
|
|
background: ${({ theme }) => theme.background.primary};
|
|
border-top: 1px solid ${({ theme }) => theme.border.color.light};
|
|
display: flex;
|
|
padding: 16px 24px 16px 48px;
|
|
width: calc(
|
|
${({ theme }) => (useIsMobile() ? '100%' : theme.rightDrawerWidth)} - 72px
|
|
);
|
|
`;
|
|
|
|
const StyledThreadCommentTitle = styled.div`
|
|
color: ${({ theme }) => theme.font.color.tertiary};
|
|
font-size: ${({ theme }) => theme.font.size.xs};
|
|
font-weight: ${({ theme }) => theme.font.weight.semiBold};
|
|
text-transform: uppercase;
|
|
`;
|
|
|
|
type ActivityCommentsProps = {
|
|
activity: Pick<Activity, 'id'>;
|
|
scrollableContainerRef: React.RefObject<HTMLDivElement>;
|
|
};
|
|
|
|
export const ActivityComments = ({
|
|
activity,
|
|
scrollableContainerRef,
|
|
}: ActivityCommentsProps) => {
|
|
const { createOneRecord: createOneComment } = useCreateOneRecord({
|
|
objectNameSingular: CoreObjectNameSingular.Comment,
|
|
});
|
|
|
|
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
|
|
|
|
const { records: comments } = useFindManyRecords({
|
|
objectNameSingular: CoreObjectNameSingular.Comment,
|
|
skip: !isNonEmptyString(activity?.id),
|
|
filter: {
|
|
activityId: {
|
|
eq: activity?.id ?? '',
|
|
},
|
|
},
|
|
});
|
|
|
|
if (!currentWorkspaceMember) {
|
|
return <></>;
|
|
}
|
|
|
|
const handleSendComment = (commentText: string) => {
|
|
if (!isNonEmptyString(commentText)) {
|
|
return;
|
|
}
|
|
|
|
createOneComment?.({
|
|
id: v4(),
|
|
authorId: currentWorkspaceMember?.id ?? '',
|
|
author: currentWorkspaceMember,
|
|
activityId: activity?.id ?? '',
|
|
body: commentText,
|
|
createdAt: new Date().toISOString(),
|
|
});
|
|
};
|
|
|
|
const handleFocus = () => {
|
|
const scrollableContainer = scrollableContainerRef.current;
|
|
|
|
scrollableContainer?.scrollTo({
|
|
top: scrollableContainer.scrollHeight,
|
|
behavior: 'smooth',
|
|
});
|
|
};
|
|
|
|
return (
|
|
<>
|
|
{comments.length > 0 && (
|
|
<>
|
|
<StyledThreadItemListContainer>
|
|
<StyledThreadCommentTitle>Comments</StyledThreadCommentTitle>
|
|
{comments?.map((comment) => (
|
|
<Comment key={comment.id} comment={comment as CommentType} />
|
|
))}
|
|
</StyledThreadItemListContainer>
|
|
</>
|
|
)}
|
|
|
|
<StyledCommentActionBar>
|
|
{currentWorkspaceMember && (
|
|
<AutosizeTextInput
|
|
onValidate={handleSendComment}
|
|
onFocus={handleFocus}
|
|
variant={AutosizeTextInputVariant.Button}
|
|
placeholder={comments.length > 0 ? 'Reply...' : undefined}
|
|
/>
|
|
)}
|
|
</StyledCommentActionBar>
|
|
</>
|
|
);
|
|
};
|