feat: rename comment thread into activity (#939)

* feat: rename commentThread into activity server

* feat: rename commentThread into activity front

* feat: migration only create tables


feat: migration only create tables

* Update activities

* fix: rebase partial fix

* fix: all rebase problems and drop activity target alter

* fix: lint

* Update migration

* Update migration

* Fix conflicts

* Fix conflicts

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Jérémy M
2023-07-28 08:22:16 +02:00
committed by GitHub
parent fcdde024a3
commit d0641084f9
95 changed files with 2112 additions and 1725 deletions

View File

@ -0,0 +1,201 @@
import React, { useEffect, useMemo, useState } from 'react';
import { getOperationName } from '@apollo/client/utilities';
import styled from '@emotion/styled';
import { ActivityBodyEditor } from '@/activities/components/ActivityBodyEditor';
import { ActivityComments } from '@/activities/components/ActivityComments';
import { ActivityRelationPicker } from '@/activities/components/ActivityRelationPicker';
import { ActivityTypeDropdown } from '@/activities/components/ActivityTypeDropdown';
import { GET_ACTIVITY } from '@/activities/queries';
import { PropertyBox } from '@/ui/editable-field/property-box/components/PropertyBox';
import { PropertyBoxItem } from '@/ui/editable-field/property-box/components/PropertyBoxItem';
import { useIsMobile } from '@/ui/hooks/useIsMobile';
import { IconArrowUpRight } from '@/ui/icon/index';
import {
useGetActivityQuery,
useUpdateActivityMutation,
} from '~/generated/graphql';
import { debounce } from '~/utils/debounce';
import { ActivityActionBar } from './ActivityActionBar';
import '@blocknote/core/style.css';
const StyledContainer = styled.div`
box-sizing: border-box;
display: flex;
flex-direction: column;
height: 100%;
justify-content: space-between;
overflow-y: auto;
`;
const StyledUpperPartContainer = styled.div`
align-items: flex-start;
box-sizing: border-box;
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(4)};
justify-content: flex-start;
`;
const StyledTopContainer = styled.div`
align-items: flex-start;
align-self: stretch;
background: ${({ theme }) => theme.background.secondary};
border-bottom: ${({ theme }) =>
useIsMobile() ? 'none' : `1px solid ${theme.border.color.medium}`};
display: flex;
flex-direction: column;
gap: 24px;
padding: 24px 24px 24px 48px;
`;
const StyledEditableTitleInput = styled.input`
background: transparent;
border: none;
color: ${({ theme }) => theme.font.color.primary};
display: flex;
flex: 1 0 0;
flex-direction: column;
font-family: Inter;
font-size: ${({ theme }) => theme.font.size.xl};
font-style: normal;
font-weight: ${({ theme }) => theme.font.weight.semiBold};
justify-content: center;
line-height: ${({ theme }) => theme.text.lineHeight.md};
outline: none;
width: calc(100% - ${({ theme }) => theme.spacing(2)});
&::placeholder {
color: ${({ theme }) => theme.font.color.light};
}
`;
const StyledTopActionsContainer = styled.div`
align-items: center;
display: flex;
flex-direction: row;
justify-content: space-between;
width: 100%;
`;
type OwnProps = {
activityId: string;
showComment?: boolean;
autoFillTitle?: boolean;
};
export function Activity({
activityId,
showComment = true,
autoFillTitle = false,
}: OwnProps) {
const { data } = useGetActivityQuery({
variables: {
activityId: activityId ?? '',
},
skip: !activityId,
});
const activity = data?.findManyActivities[0];
const [hasUserManuallySetTitle, setHasUserManuallySetTitle] =
useState<boolean>(false);
const [title, setTitle] = useState<string | null>(null);
useEffect(() => {
if (!hasUserManuallySetTitle) {
setTitle(activity?.title ?? '');
}
}, [setTitle, activity?.title, hasUserManuallySetTitle]);
const [updateActivityMutation] = useUpdateActivityMutation();
const debounceUpdateTitle = useMemo(() => {
function updateTitle(title: string) {
if (activity) {
updateActivityMutation({
variables: {
id: activityId,
title: title ?? '',
},
refetchQueries: [getOperationName(GET_ACTIVITY) ?? ''],
optimisticResponse: {
__typename: 'Mutation',
updateOneActivity: {
__typename: 'Activity',
id: activityId,
title: title,
type: activity.type,
},
},
});
}
}
return debounce(updateTitle, 200);
}, [activityId, updateActivityMutation, activity]);
function updateTitleFromBody(body: string) {
const parsedTitle = JSON.parse(body)[0]?.content[0]?.text;
if (!hasUserManuallySetTitle && autoFillTitle) {
setTitle(parsedTitle);
debounceUpdateTitle(parsedTitle);
}
}
if (!activity) {
return <></>;
}
return (
<StyledContainer>
<StyledUpperPartContainer>
<StyledTopContainer>
<StyledTopActionsContainer>
<ActivityTypeDropdown activity={activity} />
<ActivityActionBar activityId={activity?.id ?? ''} />
</StyledTopActionsContainer>
<StyledEditableTitleInput
autoFocus
placeholder={`${activity.type} title (optional)`}
onChange={(event) => {
setHasUserManuallySetTitle(true);
setTitle(event.target.value);
debounceUpdateTitle(event.target.value);
}}
value={title ?? ''}
/>
<PropertyBox>
<PropertyBoxItem
icon={<IconArrowUpRight />}
value={
<ActivityRelationPicker
activity={{
id: activity.id,
activityTargets: activity.activityTargets ?? [],
}}
/>
}
label="Relations"
/>
</PropertyBox>
</StyledTopContainer>
<ActivityBodyEditor
activity={activity}
onChange={updateTitleFromBody}
/>
</StyledUpperPartContainer>
{showComment && (
<ActivityComments
activity={{
id: activity.id,
comments: activity.comments ?? [],
}}
/>
)}
</StyledContainer>
);
}

View File

@ -3,13 +3,13 @@ import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { useRecoilState } from 'recoil';
import { GET_COMMENT_THREADS_BY_TARGETS } from '@/activities/queries';
import { GET_ACTIVITIES_BY_TARGETS } from '@/activities/queries';
import { GET_COMPANIES } from '@/companies/queries';
import { GET_PEOPLE } from '@/people/queries';
import { Button, ButtonVariant } from '@/ui/button/components/Button';
import { IconTrash } from '@/ui/icon';
import { isRightDrawerOpenState } from '@/ui/right-drawer/states/isRightDrawerOpenState';
import { useDeleteCommentThreadMutation } from '~/generated/graphql';
import { useDeleteActivityMutation } from '~/generated/graphql';
const StyledContainer = styled.div`
color: ${({ theme }) => theme.font.color.tertiary};
@ -17,21 +17,21 @@ const StyledContainer = styled.div`
`;
type OwnProps = {
commentThreadId: string;
activityId: string;
};
export function CommentThreadActionBar({ commentThreadId }: OwnProps) {
export function ActivityActionBar({ activityId }: OwnProps) {
const theme = useTheme();
const [createCommentMutation] = useDeleteCommentThreadMutation();
const [createCommentMutation] = useDeleteActivityMutation();
const [, setIsRightDrawerOpen] = useRecoilState(isRightDrawerOpenState);
function deleteCommentThread() {
function deleteActivity() {
createCommentMutation({
variables: { commentThreadId },
variables: { activityId },
refetchQueries: [
getOperationName(GET_COMPANIES) ?? '',
getOperationName(GET_PEOPLE) ?? '',
getOperationName(GET_COMMENT_THREADS_BY_TARGETS) ?? '',
getOperationName(GET_ACTIVITIES_BY_TARGETS) ?? '',
],
});
setIsRightDrawerOpen(false);
@ -43,7 +43,7 @@ export function CommentThreadActionBar({ commentThreadId }: OwnProps) {
icon={
<IconTrash size={theme.icon.size.sm} stroke={theme.icon.stroke.md} />
}
onClick={deleteCommentThread}
onClick={deleteActivity}
variant={ButtonVariant.Tertiary}
/>
</StyledContainer>

View File

@ -1,34 +0,0 @@
import { CommentThreadEditor } from '@/activities/components/CommentThreadEditor';
import { useGetCommentThreadQuery } from '~/generated/graphql';
import '@blocknote/core/style.css';
type OwnProps = {
commentThreadId: string;
showComment?: boolean;
autoFillTitle?: boolean;
};
export function CommentThread({
commentThreadId,
showComment = true,
autoFillTitle = false,
}: OwnProps) {
const { data } = useGetCommentThreadQuery({
variables: {
commentThreadId: commentThreadId ?? '',
},
skip: !commentThreadId,
});
const commentThread = data?.findManyCommentThreads[0];
return commentThread ? (
<CommentThreadEditor
commentThread={commentThread}
showComment={showComment}
autoFillTitle={autoFillTitle}
/>
) : (
<></>
);
}

View File

@ -1,22 +1,22 @@
import { useRecoilValue } from 'recoil';
import { viewableCommentThreadIdState } from '@/activities/states/viewableCommentThreadIdState';
import { viewableActivityIdState } from '@/activities/states/viewableActivityIdState';
import { RightDrawerBody } from '@/ui/right-drawer/components/RightDrawerBody';
import { RightDrawerPage } from '@/ui/right-drawer/components/RightDrawerPage';
import { RightDrawerTopBar } from '@/ui/right-drawer/components/RightDrawerTopBar';
import { CommentThread } from '../CommentThread';
import { Activity } from '../Activity';
export function RightDrawerCreateCommentThread() {
const commentThreadId = useRecoilValue(viewableCommentThreadIdState);
export function RightDrawerCreateActivity() {
const activityId = useRecoilValue(viewableActivityIdState);
return (
<RightDrawerPage>
<RightDrawerTopBar />
<RightDrawerBody>
{commentThreadId && (
<CommentThread
commentThreadId={commentThreadId}
{activityId && (
<Activity
activityId={activityId}
showComment={false}
autoFillTitle={true}
/>

View File

@ -1,20 +1,20 @@
import { useRecoilValue } from 'recoil';
import { viewableCommentThreadIdState } from '@/activities/states/viewableCommentThreadIdState';
import { viewableActivityIdState } from '@/activities/states/viewableActivityIdState';
import { RightDrawerBody } from '@/ui/right-drawer/components/RightDrawerBody';
import { RightDrawerPage } from '@/ui/right-drawer/components/RightDrawerPage';
import { RightDrawerTopBar } from '@/ui/right-drawer/components/RightDrawerTopBar';
import { CommentThread } from '../CommentThread';
import { Activity } from '../Activity';
export function RightDrawerEditCommentThread() {
const commentThreadId = useRecoilValue(viewableCommentThreadIdState);
export function RightDrawerEditActivity() {
const activityId = useRecoilValue(viewableActivityIdState);
return (
<RightDrawerPage>
<RightDrawerTopBar />
<RightDrawerBody>
{commentThreadId && <CommentThread commentThreadId={commentThreadId} />}
{activityId && <Activity activityId={activityId} />}
</RightDrawerBody>
</RightDrawerPage>
);