5622 add a syncemail onboarding step (#5689)

- add sync email onboarding step
- refactor calendar and email visibility enums
- add a new table `keyValuePair` in `core` schema
- add a new resolved boolean field `skipSyncEmail` in current user




https://github.com/twentyhq/twenty/assets/29927851/de791475-5bfe-47f9-8e90-76c349fba56f
This commit is contained in:
martmull
2024-06-05 18:16:53 +02:00
committed by GitHub
parent fda0d2a170
commit 9f6a6c3282
92 changed files with 2707 additions and 1246 deletions

View File

@ -1,7 +1,4 @@
export enum CalendarChannelVisibility {
Everything = 'SHARE_EVERYTHING',
Metadata = 'METADATA',
}
import { CalendarChannelVisibility } from '~/generated/graphql';
export type CalendarChannel = {
id: string;

View File

@ -1,11 +1,11 @@
import { InboxSettingsVisibilityValue } from '@/settings/accounts/components/SettingsAccountsInboxVisibilitySettingsCard';
import { MessageChannelVisibility } from '~/generated/graphql';
export type MessageChannel = {
id: string;
handle: string;
isContactAutoCreationEnabled?: boolean;
isSyncEnabled: boolean;
visibility: InboxSettingsVisibilityValue;
visibility: MessageChannelVisibility;
syncStatus: string;
__typename: 'MessageChannel';
};

View File

@ -14,6 +14,7 @@ import { hasCalendarEventEnded } from '@/activities/calendar/utils/hasCalendarEv
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
import { Card } from '@/ui/layout/card/components/Card';
import { CardContent } from '@/ui/layout/card/components/CardContent';
import { CalendarChannelVisibility } from '~/generated/graphql';
import { TimelineCalendarEvent } from '~/generated-metadata/graphql';
import { getImageAbsoluteURIOrBase64 } from '~/utils/image/getImageAbsoluteURIOrBase64';
import { isDefined } from '~/utils/isDefined';
@ -118,7 +119,8 @@ export const CalendarEventRow = ({
const isCurrentWorkspaceMemberAttending = calendarEvent.participants?.some(
({ workspaceMemberId }) => workspaceMemberId === currentWorkspaceMember?.id,
);
const showTitle = calendarEvent.visibility === 'SHARE_EVERYTHING';
const showTitle =
calendarEvent.visibility === CalendarChannelVisibility.ShareEverything;
return (
<StyledContainer

View File

@ -2,6 +2,7 @@ import { act, renderHook } from '@testing-library/react';
import { useCalendarEvents } from '@/activities/calendar/hooks/useCalendarEvents';
import { CalendarEvent } from '@/activities/calendar/types/CalendarEvent';
import { CalendarChannelVisibility } from '~/generated/graphql';
const calendarEvents: CalendarEvent[] = [
{
@ -9,7 +10,7 @@ const calendarEvents: CalendarEvent[] = [
externalCreatedAt: '2024-02-17T20:45:43.854Z',
isFullDay: false,
startsAt: '2024-02-17T21:45:27.822Z',
visibility: 'METADATA',
visibility: CalendarChannelVisibility.Metadata,
__typename: 'CalendarEvent',
},
{
@ -17,7 +18,7 @@ const calendarEvents: CalendarEvent[] = [
externalCreatedAt: '2024-02-18T19:43:37.854Z',
isFullDay: false,
startsAt: '2024-02-18T21:43:27.754Z',
visibility: 'SHARE_EVERYTHING',
visibility: CalendarChannelVisibility.ShareEverything,
__typename: 'CalendarEvent',
},
{
@ -25,7 +26,7 @@ const calendarEvents: CalendarEvent[] = [
externalCreatedAt: '2024-02-19T20:45:20.854Z',
isFullDay: true,
startsAt: '2024-02-19T22:05:27.653Z',
visibility: 'METADATA',
visibility: CalendarChannelVisibility.Metadata,
__typename: 'CalendarEvent',
},
{
@ -33,7 +34,7 @@ const calendarEvents: CalendarEvent[] = [
externalCreatedAt: '2024-02-20T20:45:12.854Z',
isFullDay: true,
startsAt: '2024-02-20T23:15:23.150Z',
visibility: 'SHARE_EVERYTHING',
visibility: CalendarChannelVisibility.ShareEverything,
__typename: 'CalendarEvent',
},
];

View File

@ -1,4 +1,5 @@
import { CalendarEventParticipant } from '@/activities/calendar/types/CalendarEventParticipant';
import { CalendarChannelVisibility } from '~/generated/graphql';
// TODO: use backend CalendarEvent type when ready
export type CalendarEvent = {
@ -15,7 +16,7 @@ export type CalendarEvent = {
location?: string;
startsAt: string;
title?: string;
visibility: 'METADATA' | 'SHARE_EVERYTHING';
visibility: CalendarChannelVisibility;
calendarEventParticipants?: CalendarEventParticipant[];
__typename: 'CalendarEvent';
};

View File

@ -8,18 +8,22 @@ import { useEmailThread } from '@/activities/emails/hooks/useEmailThread';
import { emailThreadIdWhenEmailThreadWasClosedState } from '@/activities/emails/states/lastViewableEmailThreadIdState';
import { CardContent } from '@/ui/layout/card/components/CardContent';
import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer';
import { TimelineThread } from '~/generated/graphql';
import { MessageChannelVisibility, TimelineThread } from '~/generated/graphql';
import { formatToHumanReadableDate } from '~/utils';
import { getImageAbsoluteURIOrBase64 } from '~/utils/image/getImageAbsoluteURIOrBase64';
const StyledCardContent = styled(CardContent)<{ visibility: string }>`
const StyledCardContent = styled(CardContent)<{
visibility: MessageChannelVisibility;
}>`
align-items: center;
display: flex;
gap: ${({ theme }) => theme.spacing(2)};
height: ${({ theme }) => theme.spacing(12)};
padding: ${({ theme }) => theme.spacing(0, 4)};
cursor: ${({ visibility }) =>
visibility === 'share_everything' ? 'pointer' : 'default'};
visibility === MessageChannelVisibility.ShareEverything
? 'pointer'
: 'default'};
`;
const StyledHeading = styled.div<{ unread: boolean }>`
@ -78,8 +82,6 @@ const StyledReceivedAt = styled.div`
padding: ${({ theme }) => theme.spacing(0, 1)};
`;
export type EmailThreadVisibility = 'metadata' | 'subject' | 'share_everything';
type EmailThreadPreviewProps = {
divider?: boolean;
thread: TimelineThread;
@ -93,7 +95,7 @@ export const EmailThreadPreview = ({
const { openEmailThread } = useEmailThread();
const visibility = thread.visibility as EmailThreadVisibility;
const visibility = thread.visibility;
const senderNames =
thread.firstParticipant.displayName +
@ -126,7 +128,7 @@ export const EmailThreadPreview = ({
.getValue();
const canOpen =
thread.visibility === 'share_everything' &&
thread.visibility === MessageChannelVisibility.ShareEverything &&
(!clickJustTriggeredEmailDrawerClose ||
emailThreadIdWhenEmailThreadWasClosed !== thread.id);
@ -183,13 +185,15 @@ export const EmailThreadPreview = ({
</StyledHeading>
<StyledSubjectAndBody>
{visibility !== 'metadata' && (
{visibility !== MessageChannelVisibility.Metadata && (
<StyledSubject>{thread.subject}</StyledSubject>
)}
{visibility === 'share_everything' && (
{visibility === MessageChannelVisibility.ShareEverything && (
<StyledBody>{thread.lastMessageBody}</StyledBody>
)}
{visibility !== 'share_everything' && <EmailThreadNotShared />}
{visibility !== MessageChannelVisibility.ShareEverything && (
<EmailThreadNotShared />
)}
</StyledSubjectAndBody>
<StyledReceivedAt>
{formatToHumanReadableDate(thread.lastMessageReceivedAt)}

View File

@ -3,6 +3,7 @@ import { renderHook } from '@testing-library/react';
import { RecoilRoot, useSetRecoilState } from 'recoil';
import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus';
import { CurrentUser, currentUserState } from '@/auth/states/currentUserState';
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
import {
CurrentWorkspace,
@ -20,6 +21,13 @@ const billing = {
billingUrl: 'testing.com',
isBillingEnabled: true,
};
const currentUser = {
id: '1',
email: 'test@test',
supportUserHash: '1',
canImpersonate: false,
state: { skipSyncEmailOnboardingStep: true },
} as CurrentUser;
const currentWorkspace = {
activationStatus: 'active',
id: '1',
@ -27,7 +35,7 @@ const currentWorkspace = {
currentBillingSubscription: {
status: 'trialing',
},
};
} as CurrentWorkspace;
const currentWorkspaceMember = {
id: '1',
locale: '',
@ -46,12 +54,14 @@ const renderHooks = () => {
const setCurrentWorkspaceMember = useSetRecoilState(
currentWorkspaceMemberState,
);
const setCurrentUser = useSetRecoilState(currentUserState);
const setTokenPair = useSetRecoilState(tokenPairState);
const setVerifyPending = useSetRecoilState(isVerifyPendingState);
return {
onboardingStatus,
setBilling,
setCurrentUser,
setCurrentWorkspace,
setCurrentWorkspaceMember,
setTokenPair,
@ -77,6 +87,7 @@ describe('useOnboardingStatus', () => {
const {
setTokenPair,
setBilling,
setCurrentUser,
setCurrentWorkspace,
setCurrentWorkspaceMember,
} = result.current;
@ -84,10 +95,11 @@ describe('useOnboardingStatus', () => {
act(() => {
setTokenPair(tokenPair);
setBilling(billing);
setCurrentUser(currentUser);
setCurrentWorkspace({
...currentWorkspace,
subscriptionStatus: 'incomplete',
} as CurrentWorkspace);
});
setCurrentWorkspaceMember(currentWorkspaceMember);
});
@ -99,6 +111,7 @@ describe('useOnboardingStatus', () => {
const {
setTokenPair,
setBilling,
setCurrentUser,
setCurrentWorkspace,
setCurrentWorkspaceMember,
} = result.current;
@ -106,10 +119,11 @@ describe('useOnboardingStatus', () => {
act(() => {
setTokenPair(tokenPair);
setBilling(billing);
setCurrentUser(currentUser);
setCurrentWorkspace({
...currentWorkspace,
subscriptionStatus: 'canceled',
} as CurrentWorkspace);
});
setCurrentWorkspaceMember({
...currentWorkspaceMember,
name: {
@ -124,16 +138,18 @@ describe('useOnboardingStatus', () => {
it('should return "ongoing_workspace_activation"', async () => {
const { result } = renderHooks();
const { setTokenPair, setBilling, setCurrentWorkspace } = result.current;
const { setTokenPair, setBilling, setCurrentUser, setCurrentWorkspace } =
result.current;
act(() => {
setTokenPair(tokenPair);
setBilling(billing);
setCurrentUser(currentUser);
setCurrentWorkspace({
...currentWorkspace,
activationStatus: 'inactive',
subscriptionStatus: 'active',
} as CurrentWorkspace);
});
});
expect(result.current.onboardingStatus).toBe(
@ -146,6 +162,7 @@ describe('useOnboardingStatus', () => {
const {
setTokenPair,
setBilling,
setCurrentUser,
setCurrentWorkspace,
setCurrentWorkspaceMember,
} = result.current;
@ -153,21 +170,56 @@ describe('useOnboardingStatus', () => {
act(() => {
setTokenPair(tokenPair);
setBilling(billing);
setCurrentUser(currentUser);
setCurrentWorkspace({
...currentWorkspace,
subscriptionStatus: 'active',
} as CurrentWorkspace);
});
setCurrentWorkspaceMember(currentWorkspaceMember);
});
expect(result.current.onboardingStatus).toBe('ongoing_profile_creation');
});
it('should return "ongoing_sync_email"', async () => {
const { result } = renderHooks();
const {
setTokenPair,
setBilling,
setCurrentUser,
setCurrentWorkspace,
setCurrentWorkspaceMember,
} = result.current;
act(() => {
setTokenPair(tokenPair);
setBilling(billing);
setCurrentUser({
...currentUser,
state: { skipSyncEmailOnboardingStep: false },
});
setCurrentWorkspace({
...currentWorkspace,
subscriptionStatus: 'active',
});
setCurrentWorkspaceMember({
...currentWorkspaceMember,
name: {
firstName: 'John',
lastName: 'Doe',
},
});
});
expect(result.current.onboardingStatus).toBe('ongoing_sync_email');
});
it('should return "completed"', async () => {
const { result } = renderHooks();
const {
setTokenPair,
setBilling,
setCurrentUser,
setCurrentWorkspace,
setCurrentWorkspaceMember,
} = result.current;
@ -175,10 +227,11 @@ describe('useOnboardingStatus', () => {
act(() => {
setTokenPair(tokenPair);
setBilling(billing);
setCurrentUser(currentUser);
setCurrentWorkspace({
...currentWorkspace,
subscriptionStatus: 'active',
} as CurrentWorkspace);
});
setCurrentWorkspaceMember({
...currentWorkspaceMember,
name: {
@ -196,6 +249,7 @@ describe('useOnboardingStatus', () => {
const {
setTokenPair,
setBilling,
setCurrentUser,
setCurrentWorkspace,
setCurrentWorkspaceMember,
} = result.current;
@ -203,10 +257,11 @@ describe('useOnboardingStatus', () => {
act(() => {
setTokenPair(tokenPair);
setBilling(billing);
setCurrentUser(currentUser);
setCurrentWorkspace({
...currentWorkspace,
subscriptionStatus: 'past_due',
} as CurrentWorkspace);
});
setCurrentWorkspaceMember({
...currentWorkspaceMember,
name: {
@ -224,6 +279,7 @@ describe('useOnboardingStatus', () => {
const {
setTokenPair,
setBilling,
setCurrentUser,
setCurrentWorkspace,
setCurrentWorkspaceMember,
} = result.current;
@ -231,10 +287,11 @@ describe('useOnboardingStatus', () => {
act(() => {
setTokenPair(tokenPair);
setBilling(billing);
setCurrentUser(currentUser);
setCurrentWorkspace({
...currentWorkspace,
subscriptionStatus: 'unpaid',
} as CurrentWorkspace);
});
setCurrentWorkspaceMember({
...currentWorkspaceMember,
name: {
@ -252,6 +309,7 @@ describe('useOnboardingStatus', () => {
const {
setTokenPair,
setBilling,
setCurrentUser,
setCurrentWorkspace,
setCurrentWorkspaceMember,
} = result.current;
@ -259,6 +317,7 @@ describe('useOnboardingStatus', () => {
act(() => {
setTokenPair(tokenPair);
setBilling(billing);
setCurrentUser(currentUser);
setCurrentWorkspace({
...currentWorkspace,
subscriptionStatus: 'trialing',

View File

@ -1,5 +1,6 @@
import { useRecoilValue } from 'recoil';
import { currentUserState } from '@/auth/states/currentUserState';
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { billingState } from '@/client-config/states/billingState';
@ -14,12 +15,14 @@ export const useOnboardingStatus = (): OnboardingStatus | undefined => {
const billing = useRecoilValue(billingState);
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
const currentWorkspace = useRecoilValue(currentWorkspaceState);
const currentUser = useRecoilValue(currentUserState);
const isLoggedIn = useIsLogged();
return getOnboardingStatus({
isLoggedIn,
currentWorkspaceMember,
currentWorkspace,
currentUser,
isBillingEnabled: billing?.isBillingEnabled || false,
});
};

View File

@ -4,7 +4,7 @@ import { User } from '~/generated/graphql';
export type CurrentUser = Pick<
User,
'id' | 'email' | 'supportUserHash' | 'canImpersonate'
'id' | 'email' | 'supportUserHash' | 'canImpersonate' | 'state'
>;
export const currentUserState = createState<CurrentUser | null>({

View File

@ -1,3 +1,4 @@
import { CurrentUser } from '@/auth/states/currentUserState';
import { CurrentWorkspace } from '@/auth/states/currentWorkspaceState';
import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember';
@ -9,6 +10,7 @@ describe('getOnboardingStatus', () => {
isLoggedIn: false,
currentWorkspaceMember: null,
currentWorkspace: null,
currentUser: null,
isBillingEnabled: false,
});
@ -19,6 +21,9 @@ describe('getOnboardingStatus', () => {
id: '1',
activationStatus: 'inactive',
} as CurrentWorkspace,
currentUser: {
state: { skipSyncEmailOnboardingStep: true },
} as CurrentUser,
isBillingEnabled: false,
});
@ -32,6 +37,28 @@ describe('getOnboardingStatus', () => {
id: '1',
activationStatus: 'active',
} as CurrentWorkspace,
currentUser: {
state: { skipSyncEmailOnboardingStep: true },
} as CurrentUser,
isBillingEnabled: false,
});
const ongoingSyncEmail = getOnboardingStatus({
isLoggedIn: true,
currentWorkspaceMember: {
id: '1',
name: {
firstName: 'John',
lastName: 'Doe',
},
} as WorkspaceMember,
currentWorkspace: {
id: '1',
activationStatus: 'active',
} as CurrentWorkspace,
currentUser: {
state: { skipSyncEmailOnboardingStep: false },
} as CurrentUser,
isBillingEnabled: false,
});
@ -48,6 +75,9 @@ describe('getOnboardingStatus', () => {
id: '1',
activationStatus: 'active',
} as CurrentWorkspace,
currentUser: {
state: { skipSyncEmailOnboardingStep: true },
} as CurrentUser,
isBillingEnabled: false,
});
@ -65,6 +95,9 @@ describe('getOnboardingStatus', () => {
activationStatus: 'active',
subscriptionStatus: 'incomplete',
} as CurrentWorkspace,
currentUser: {
state: { skipSyncEmailOnboardingStep: true },
} as CurrentUser,
isBillingEnabled: true,
});
@ -82,6 +115,9 @@ describe('getOnboardingStatus', () => {
activationStatus: 'active',
subscriptionStatus: 'incomplete',
} as CurrentWorkspace,
currentUser: {
state: { skipSyncEmailOnboardingStep: true },
} as CurrentUser,
isBillingEnabled: false,
});
@ -99,12 +135,16 @@ describe('getOnboardingStatus', () => {
activationStatus: 'active',
subscriptionStatus: 'canceled',
} as CurrentWorkspace,
currentUser: {
state: { skipSyncEmailOnboardingStep: true },
} as CurrentUser,
isBillingEnabled: true,
});
expect(ongoingUserCreation).toBe('ongoing_user_creation');
expect(ongoingWorkspaceActivation).toBe('ongoing_workspace_activation');
expect(ongoingProfileCreation).toBe('ongoing_profile_creation');
expect(ongoingSyncEmail).toBe('ongoing_sync_email');
expect(completed).toBe('completed');
expect(incomplete).toBe('incomplete');
expect(canceled).toBe('canceled');

View File

@ -1,3 +1,4 @@
import { CurrentUser } from '@/auth/states/currentUserState';
import { CurrentWorkspace } from '@/auth/states/currentWorkspaceState';
import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember';
@ -9,6 +10,7 @@ export enum OnboardingStatus {
OngoingUserCreation = 'ongoing_user_creation',
OngoingWorkspaceActivation = 'ongoing_workspace_activation',
OngoingProfileCreation = 'ongoing_profile_creation',
OngoingSyncEmail = 'ongoing_sync_email',
Completed = 'completed',
CompletedWithoutSubscription = 'completed_without_subscription',
}
@ -17,6 +19,7 @@ export const getOnboardingStatus = ({
isLoggedIn,
currentWorkspaceMember,
currentWorkspace,
currentUser,
isBillingEnabled,
}: {
isLoggedIn: boolean;
@ -25,6 +28,7 @@ export const getOnboardingStatus = ({
'createdAt' | 'updatedAt' | 'userId' | 'userEmail' | '__typename'
> | null;
currentWorkspace: CurrentWorkspace | null;
currentUser: CurrentUser | null;
isBillingEnabled: boolean;
}) => {
if (!isLoggedIn) {
@ -33,7 +37,7 @@ export const getOnboardingStatus = ({
// After SignInUp, the user should have a current workspace assigned.
// If not, it indicates that the data is still being requested.
if (!currentWorkspace) {
if (!currentWorkspace || !currentUser) {
return undefined;
}
@ -55,6 +59,10 @@ export const getOnboardingStatus = ({
return OnboardingStatus.OngoingProfileCreation;
}
if (!currentUser.state.skipSyncEmailOnboardingStep) {
return OnboardingStatus.OngoingSyncEmail;
}
if (isBillingEnabled && currentWorkspace.subscriptionStatus === 'canceled') {
return OnboardingStatus.Canceled;
}

View File

@ -292,6 +292,66 @@ export const getObjectMetadataItemsMock = () => {
updatedAt: '2023-11-30T11:13:15.206Z',
fields: [],
},
{
__typename: 'object',
id: '3aac4582-f677-4d7d-acd5-3e33a039acdd',
dataSourceId: '20202020-7f63-47a9-b1b3-6c7290ca9fb1',
nameSingular: 'connectedAccount',
namePlural: 'connectedAccounts',
labelSingular: 'Connected Account',
labelPlural: 'Connected Accounts',
description: 'A connected account',
icon: 'IconAt',
isCustom: false,
isRemote: false,
isActive: true,
isSystem: true,
createdAt: '2024-06-01T14:55:04.039Z',
updatedAt: '2024-06-01T14:55:04.039Z',
labelIdentifierFieldMetadataId: null,
imageIdentifierFieldMetadataId: null,
fields: [],
},
{
__typename: 'object',
id: '3aac4582-f677-4d7d-acd5-3e33a039acde',
dataSourceId: '20202020-7f63-47a9-b1b3-6c7290ca9fb1',
nameSingular: 'messageChannel',
namePlural: 'messageChannels',
labelSingular: 'Message Channel',
labelPlural: 'Message Channels',
description: 'A message channel',
icon: 'IconAt',
isCustom: false,
isRemote: false,
isActive: true,
isSystem: true,
createdAt: '2024-06-01T14:55:04.039Z',
updatedAt: '2024-06-01T14:55:04.039Z',
labelIdentifierFieldMetadataId: null,
imageIdentifierFieldMetadataId: null,
fields: [],
},
{
__typename: 'object',
id: '3aac4582-f677-4d7d-acd5-3e33a039acdf',
dataSourceId: '20202020-7f63-47a9-b1b3-6c7290ca9fb1',
nameSingular: 'calendarChannel',
namePlural: 'calendarChannels',
labelSingular: 'Calendar Channel',
labelPlural: 'Calendar Channels',
description: 'A calendar channel',
icon: 'IconAt',
isCustom: false,
isRemote: false,
isActive: true,
isSystem: true,
createdAt: '2024-06-01T14:55:04.039Z',
updatedAt: '2024-06-01T14:55:04.039Z',
labelIdentifierFieldMetadataId: null,
imageIdentifierFieldMetadataId: null,
fields: [],
},
{
__typename: 'object',
id: '20202020-cae9-4ff4-9579-f7d9fe44c937',

View File

@ -0,0 +1,19 @@
import { onboardingSyncEmailsOptions } from '@/onboarding/components/onboardingSyncEmailsOptions';
import { SettingsAccountsRadioSettingsCard } from '@/settings/accounts/components/SettingsAccountsRadioSettingsCard';
import { MessageChannelVisibility } from '~/generated/graphql';
type OnboardingSyncEmailsSettingsCardProps = {
onChange: (nextValue: MessageChannelVisibility) => void;
value?: MessageChannelVisibility;
};
export const OnboardingSyncEmailsSettingsCard = ({
onChange,
value = MessageChannelVisibility.ShareEverything,
}: OnboardingSyncEmailsSettingsCardProps) => (
<SettingsAccountsRadioSettingsCard
options={onboardingSyncEmailsOptions}
value={value}
onChange={onChange}
/>
);

View File

@ -0,0 +1,38 @@
import styled from '@emotion/styled';
import { SettingsAccountsVisibilitySettingCardMedia } from '@/settings/accounts/components/SettingsAccountsVisibilitySettingCardMedia';
import { MessageChannelVisibility } from '~/generated/graphql';
const StyledCardMedia = styled(SettingsAccountsVisibilitySettingCardMedia)`
width: ${({ theme }) => theme.spacing(10)};
`;
export const onboardingSyncEmailsOptions = [
{
title: 'Everything',
description:
'Your emails and events content will be shared with your team.',
value: MessageChannelVisibility.ShareEverything,
cardMedia: (
<StyledCardMedia metadata="active" subject="active" body="active" />
),
},
{
title: 'Subject and metadata',
description:
'Your email subjects and meeting titles will be shared with your team.',
value: MessageChannelVisibility.Subject,
cardMedia: (
<StyledCardMedia metadata="active" subject="active" body="inactive" />
),
},
{
title: 'Metadata',
description:
'Only the timestamp & participants will be shared with your team.',
value: MessageChannelVisibility.Metadata,
cardMedia: (
<StyledCardMedia metadata="active" subject="inactive" body="inactive" />
),
},
];

View File

@ -0,0 +1,9 @@
import { gql } from '@apollo/client';
export const SKIP_SYNC_EMAIL_ONBOARDING_STEP = gql`
mutation SkipSyncEmailOnboardingStep {
skipSyncEmailOnboardingStep {
success
}
}
`;

View File

@ -1,8 +1,8 @@
import styled from '@emotion/styled';
import { CalendarChannelVisibility } from '@/accounts/types/CalendarChannel';
import { SettingsAccountsRadioSettingsCard } from '@/settings/accounts/components/SettingsAccountsRadioSettingsCard';
import { SettingsAccountsVisibilitySettingCardMedia } from '@/settings/accounts/components/SettingsAccountsVisibilitySettingCardMedia';
import { CalendarChannelVisibility } from '~/generated/graphql';
type SettingsAccountsEventVisibilitySettingsCardProps = {
onChange: (nextValue: CalendarChannelVisibility) => void;
@ -17,7 +17,7 @@ const eventSettingsVisibilityOptions = [
{
title: 'Everything',
description: 'The whole event details will be shared with your team.',
value: CalendarChannelVisibility.Everything,
value: CalendarChannelVisibility.ShareEverything,
cardMedia: <StyledCardMedia subject="active" body="active" />,
},
{
@ -30,7 +30,7 @@ const eventSettingsVisibilityOptions = [
export const SettingsAccountsEventVisibilitySettingsCard = ({
onChange,
value = CalendarChannelVisibility.Everything,
value = CalendarChannelVisibility.ShareEverything,
}: SettingsAccountsEventVisibilitySettingsCardProps) => (
<SettingsAccountsRadioSettingsCard
options={eventSettingsVisibilityOptions}

View File

@ -1,22 +1,17 @@
import { SettingsAccountsRadioSettingsCard } from '@/settings/accounts/components/SettingsAccountsRadioSettingsCard';
import { SettingsAccountsVisibilitySettingCardMedia } from '@/settings/accounts/components/SettingsAccountsVisibilitySettingCardMedia';
export enum InboxSettingsVisibilityValue {
Everything = 'share_everything',
SubjectMetadata = 'subject',
Metadata = 'metadata',
}
import { MessageChannelVisibility } from '~/generated/graphql';
type SettingsAccountsInboxVisibilitySettingsCardProps = {
onChange: (nextValue: InboxSettingsVisibilityValue) => void;
value?: InboxSettingsVisibilityValue;
onChange: (nextValue: MessageChannelVisibility) => void;
value?: MessageChannelVisibility;
};
const inboxSettingsVisibilityOptions = [
{
title: 'Everything',
description: 'Subject, body and attachments will be shared with your team.',
value: InboxSettingsVisibilityValue.Everything,
value: MessageChannelVisibility.ShareEverything,
cardMedia: (
<SettingsAccountsVisibilitySettingCardMedia
metadata="active"
@ -28,7 +23,7 @@ const inboxSettingsVisibilityOptions = [
{
title: 'Subject and metadata',
description: 'Subject and metadata will be shared with your team.',
value: InboxSettingsVisibilityValue.SubjectMetadata,
value: MessageChannelVisibility.Subject,
cardMedia: (
<SettingsAccountsVisibilitySettingCardMedia
metadata="active"
@ -40,7 +35,7 @@ const inboxSettingsVisibilityOptions = [
{
title: 'Metadata',
description: 'Timestamp and participants will be shared with your team.',
value: InboxSettingsVisibilityValue.Metadata,
value: MessageChannelVisibility.Metadata,
cardMedia: (
<SettingsAccountsVisibilitySettingCardMedia
metadata="active"
@ -53,7 +48,7 @@ const inboxSettingsVisibilityOptions = [
export const SettingsAccountsInboxVisibilitySettingsCard = ({
onChange,
value = InboxSettingsVisibilityValue.Everything,
value = MessageChannelVisibility.ShareEverything,
}: SettingsAccountsInboxVisibilitySettingsCardProps) => (
<SettingsAccountsRadioSettingsCard
options={inboxSettingsVisibilityOptions}

View File

@ -27,6 +27,10 @@ export const SettingsAccountsListEmptyStateCard = ({
}: SettingsAccountsListEmptyStateCardProps) => {
const { triggerGoogleApisOAuth } = useTriggerGoogleApisOAuth();
const handleOnClick = async () => {
await triggerGoogleApisOAuth();
};
return (
<Card>
<StyledHeader>{label || 'No connected account'}</StyledHeader>
@ -35,7 +39,7 @@ export const SettingsAccountsListEmptyStateCard = ({
Icon={IconGoogle}
title="Connect with Google"
variant="secondary"
onClick={triggerGoogleApisOAuth}
onClick={handleOnClick}
/>
</StyledBody>
</Card>

View File

@ -50,7 +50,7 @@ export const SettingsAccountsRadioSettingsCard = <
options,
value,
}: SettingsAccountsRadioSettingsCardProps<Option>) => (
<Card>
<Card rounded>
{options.map((option, index) => (
<StyledCardContent
key={option.value}

View File

@ -36,7 +36,7 @@ export const SettingsAccountsToggleSettingCard = ({
onToggle,
title,
}: SettingsAccountsToggleSettingCardProps) => (
<Card>
<Card rounded>
<StyledCardContent onClick={() => onToggle(!value)}>
{cardMedia}
<StyledTitle>{title}</StyledTitle>

View File

@ -1,21 +1,47 @@
import { useCallback } from 'react';
import { AppPath } from '@/types/AppPath';
import { REACT_APP_SERVER_BASE_URL } from '~/config';
import { useGenerateTransientTokenMutation } from '~/generated/graphql';
import {
CalendarChannelVisibility,
MessageChannelVisibility,
useGenerateTransientTokenMutation,
} from '~/generated/graphql';
export const useTriggerGoogleApisOAuth = () => {
const [generateTransientToken] = useGenerateTransientTokenMutation();
const triggerGoogleApisOAuth = useCallback(async () => {
const authServerUrl = REACT_APP_SERVER_BASE_URL;
const triggerGoogleApisOAuth = useCallback(
async (
redirectLocation?: AppPath,
messageVisibility?: MessageChannelVisibility,
calendarVisibility?: CalendarChannelVisibility,
) => {
const authServerUrl = REACT_APP_SERVER_BASE_URL;
const transientToken = await generateTransientToken();
const transientToken = await generateTransientToken();
const token =
transientToken.data?.generateTransientToken.transientToken.token;
const token =
transientToken.data?.generateTransientToken.transientToken.token;
window.location.href = `${authServerUrl}/auth/google-apis?transientToken=${token}`;
}, [generateTransientToken]);
let params = `transientToken=${token}`;
params += redirectLocation
? `&redirectLocation=${encodeURIComponent(redirectLocation)}`
: '';
params += calendarVisibility
? `&calendarVisibility=${calendarVisibility}`
: '';
params += messageVisibility
? `&messageVisibility=${messageVisibility}`
: '';
window.location.href = `${authServerUrl}/auth/google-apis?${params}`;
},
[generateTransientToken],
);
return { triggerGoogleApisOAuth };
};

View File

@ -8,6 +8,7 @@ export enum AppPath {
// Onboarding
CreateWorkspace = '/create/workspace',
CreateProfile = '/create/profile',
SyncEmails = '/sync/emails',
PlanRequired = '/plan-required',
PlanRequiredSuccess = '/plan-required/payment-success',

View File

@ -1,8 +1,9 @@
import styled from '@emotion/styled';
const StyledCard = styled.div<{ fullWidth?: boolean }>`
const StyledCard = styled.div<{ fullWidth?: boolean; rounded?: boolean }>`
border: 1px solid ${({ theme }) => theme.border.color.medium};
border-radius: ${({ theme }) => theme.border.radius.sm};
border-radius: ${({ theme, rounded }) =>
rounded ? theme.border.radius.md : theme.border.radius.sm};
color: ${({ theme }) => theme.font.color.secondary};
overflow: hidden;
width: ${({ fullWidth }) => (fullWidth ? '100%' : 'auto')};

View File

@ -46,6 +46,7 @@ const testCases = [
{ loc: AppPath.Verify, status: OnboardingStatus.OngoingUserCreation, res: false },
{ loc: AppPath.Verify, status: OnboardingStatus.OngoingWorkspaceActivation, res: false },
{ loc: AppPath.Verify, status: OnboardingStatus.OngoingProfileCreation, res: false },
{ loc: AppPath.Verify, status: OnboardingStatus.OngoingSyncEmail, res: false },
{ loc: AppPath.Verify, status: OnboardingStatus.Completed, res: false },
{ loc: AppPath.Verify, status: OnboardingStatus.CompletedWithoutSubscription, res: false },
@ -56,6 +57,7 @@ const testCases = [
{ loc: AppPath.SignInUp, status: OnboardingStatus.OngoingUserCreation, res: true },
{ loc: AppPath.SignInUp, status: OnboardingStatus.OngoingWorkspaceActivation, res: true },
{ loc: AppPath.SignInUp, status: OnboardingStatus.OngoingProfileCreation, res: true },
{ loc: AppPath.SignInUp, status: OnboardingStatus.OngoingSyncEmail, res: true },
{ loc: AppPath.SignInUp, status: OnboardingStatus.Completed, res: false },
{ loc: AppPath.SignInUp, status: OnboardingStatus.CompletedWithoutSubscription, res: false },
@ -66,6 +68,7 @@ const testCases = [
{ loc: AppPath.Invite, status: OnboardingStatus.OngoingUserCreation, res: true },
{ loc: AppPath.Invite, status: OnboardingStatus.OngoingWorkspaceActivation, res: true },
{ loc: AppPath.Invite, status: OnboardingStatus.OngoingProfileCreation, res: true },
{ loc: AppPath.Invite, status: OnboardingStatus.OngoingSyncEmail, res: true },
{ loc: AppPath.Invite, status: OnboardingStatus.Completed, res: true },
{ loc: AppPath.Invite, status: OnboardingStatus.CompletedWithoutSubscription, res: true },
@ -76,6 +79,7 @@ const testCases = [
{ loc: AppPath.ResetPassword, status: OnboardingStatus.OngoingUserCreation, res: true },
{ loc: AppPath.ResetPassword, status: OnboardingStatus.OngoingWorkspaceActivation, res: true },
{ loc: AppPath.ResetPassword, status: OnboardingStatus.OngoingProfileCreation, res: true },
{ loc: AppPath.ResetPassword, status: OnboardingStatus.OngoingSyncEmail, res: true },
{ loc: AppPath.ResetPassword, status: OnboardingStatus.Completed, res: true },
{ loc: AppPath.ResetPassword, status: OnboardingStatus.CompletedWithoutSubscription, res: true },
@ -86,6 +90,7 @@ const testCases = [
{ loc: AppPath.CreateWorkspace, status: OnboardingStatus.OngoingUserCreation, res: true },
{ loc: AppPath.CreateWorkspace, status: OnboardingStatus.OngoingWorkspaceActivation, res: true },
{ loc: AppPath.CreateWorkspace, status: OnboardingStatus.OngoingProfileCreation, res: true },
{ loc: AppPath.CreateWorkspace, status: OnboardingStatus.OngoingSyncEmail, res: true },
{ loc: AppPath.CreateWorkspace, status: OnboardingStatus.Completed, res: false },
{ loc: AppPath.CreateWorkspace, status: OnboardingStatus.CompletedWithoutSubscription, res: false },
@ -96,9 +101,21 @@ const testCases = [
{ loc: AppPath.CreateProfile, status: OnboardingStatus.OngoingUserCreation, res: true },
{ loc: AppPath.CreateProfile, status: OnboardingStatus.OngoingWorkspaceActivation, res: true },
{ loc: AppPath.CreateProfile, status: OnboardingStatus.OngoingProfileCreation, res: true },
{ loc: AppPath.CreateProfile, status: OnboardingStatus.OngoingSyncEmail, res: true },
{ loc: AppPath.CreateProfile, status: OnboardingStatus.Completed, res: false },
{ loc: AppPath.CreateProfile, status: OnboardingStatus.CompletedWithoutSubscription, res: false },
{ loc: AppPath.SyncEmails, status: OnboardingStatus.Incomplete, res: true },
{ loc: AppPath.SyncEmails, status: OnboardingStatus.Canceled, res: false },
{ loc: AppPath.SyncEmails, status: OnboardingStatus.Unpaid, res: false },
{ loc: AppPath.SyncEmails, status: OnboardingStatus.PastDue, res: false },
{ loc: AppPath.SyncEmails, status: OnboardingStatus.OngoingUserCreation, res: true },
{ loc: AppPath.SyncEmails, status: OnboardingStatus.OngoingWorkspaceActivation, res: true },
{ loc: AppPath.SyncEmails, status: OnboardingStatus.OngoingProfileCreation, res: true },
{ loc: AppPath.SyncEmails, status: OnboardingStatus.OngoingSyncEmail, res: true },
{ loc: AppPath.SyncEmails, status: OnboardingStatus.Completed, res: false },
{ loc: AppPath.SyncEmails, status: OnboardingStatus.CompletedWithoutSubscription, res: false },
{ loc: AppPath.PlanRequired, status: OnboardingStatus.Incomplete, res: true },
{ loc: AppPath.PlanRequired, status: OnboardingStatus.Canceled, res: true },
{ loc: AppPath.PlanRequired, status: OnboardingStatus.Unpaid, res: false },
@ -106,6 +123,7 @@ const testCases = [
{ loc: AppPath.PlanRequired, status: OnboardingStatus.OngoingUserCreation, res: true },
{ loc: AppPath.PlanRequired, status: OnboardingStatus.OngoingWorkspaceActivation, res: true },
{ loc: AppPath.PlanRequired, status: OnboardingStatus.OngoingProfileCreation, res: true },
{ loc: AppPath.PlanRequired, status: OnboardingStatus.OngoingSyncEmail, res: true },
{ loc: AppPath.PlanRequired, status: OnboardingStatus.Completed, res: false },
{ loc: AppPath.PlanRequired, status: OnboardingStatus.CompletedWithoutSubscription, res: true },
@ -116,6 +134,7 @@ const testCases = [
{ loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.OngoingUserCreation, res: true },
{ loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.OngoingWorkspaceActivation, res: true },
{ loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.OngoingProfileCreation, res: true },
{ loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.OngoingSyncEmail, res: true },
{ loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.Completed, res: false },
{ loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.CompletedWithoutSubscription, res: false },
@ -126,6 +145,7 @@ const testCases = [
{ loc: AppPath.Index, status: OnboardingStatus.OngoingUserCreation, res: true },
{ loc: AppPath.Index, status: OnboardingStatus.OngoingWorkspaceActivation, res: true },
{ loc: AppPath.Index, status: OnboardingStatus.OngoingProfileCreation, res: true },
{ loc: AppPath.Index, status: OnboardingStatus.OngoingSyncEmail, res: true },
{ loc: AppPath.Index, status: OnboardingStatus.Completed, res: false },
{ loc: AppPath.Index, status: OnboardingStatus.CompletedWithoutSubscription, res: false },
@ -136,6 +156,7 @@ const testCases = [
{ loc: AppPath.TasksPage, status: OnboardingStatus.OngoingUserCreation, res: true },
{ loc: AppPath.TasksPage, status: OnboardingStatus.OngoingWorkspaceActivation, res: true },
{ loc: AppPath.TasksPage, status: OnboardingStatus.OngoingProfileCreation, res: true },
{ loc: AppPath.TasksPage, status: OnboardingStatus.OngoingSyncEmail, res: true },
{ loc: AppPath.TasksPage, status: OnboardingStatus.Completed, res: false },
{ loc: AppPath.TasksPage, status: OnboardingStatus.CompletedWithoutSubscription, res: false },
@ -146,6 +167,7 @@ const testCases = [
{ loc: AppPath.OpportunitiesPage, status: OnboardingStatus.OngoingUserCreation, res: true },
{ loc: AppPath.OpportunitiesPage, status: OnboardingStatus.OngoingWorkspaceActivation, res: true },
{ loc: AppPath.OpportunitiesPage, status: OnboardingStatus.OngoingProfileCreation, res: true },
{ loc: AppPath.OpportunitiesPage, status: OnboardingStatus.OngoingSyncEmail, res: true },
{ loc: AppPath.OpportunitiesPage, status: OnboardingStatus.Completed, res: false },
{ loc: AppPath.OpportunitiesPage, status: OnboardingStatus.CompletedWithoutSubscription, res: false },
@ -156,6 +178,7 @@ const testCases = [
{ loc: AppPath.RecordIndexPage, status: OnboardingStatus.OngoingUserCreation, res: true },
{ loc: AppPath.RecordIndexPage, status: OnboardingStatus.OngoingWorkspaceActivation, res: true },
{ loc: AppPath.RecordIndexPage, status: OnboardingStatus.OngoingProfileCreation, res: true },
{ loc: AppPath.RecordIndexPage, status: OnboardingStatus.OngoingSyncEmail, res: true },
{ loc: AppPath.RecordIndexPage, status: OnboardingStatus.Completed, res: false },
{ loc: AppPath.RecordIndexPage, status: OnboardingStatus.CompletedWithoutSubscription, res: false },
@ -166,6 +189,7 @@ const testCases = [
{ loc: AppPath.RecordShowPage, status: OnboardingStatus.OngoingUserCreation, res: true },
{ loc: AppPath.RecordShowPage, status: OnboardingStatus.OngoingWorkspaceActivation, res: true },
{ loc: AppPath.RecordShowPage, status: OnboardingStatus.OngoingProfileCreation, res: true },
{ loc: AppPath.RecordShowPage, status: OnboardingStatus.OngoingSyncEmail, res: true },
{ loc: AppPath.RecordShowPage, status: OnboardingStatus.Completed, res: false },
{ loc: AppPath.RecordShowPage, status: OnboardingStatus.CompletedWithoutSubscription, res: false },
@ -176,6 +200,7 @@ const testCases = [
{ loc: AppPath.SettingsCatchAll, status: OnboardingStatus.OngoingUserCreation, res: true },
{ loc: AppPath.SettingsCatchAll, status: OnboardingStatus.OngoingWorkspaceActivation, res: true },
{ loc: AppPath.SettingsCatchAll, status: OnboardingStatus.OngoingProfileCreation, res: true },
{ loc: AppPath.SettingsCatchAll, status: OnboardingStatus.OngoingSyncEmail, res: true },
{ loc: AppPath.SettingsCatchAll, status: OnboardingStatus.Completed, res: false },
{ loc: AppPath.SettingsCatchAll, status: OnboardingStatus.CompletedWithoutSubscription, res: false },
@ -186,6 +211,7 @@ const testCases = [
{ loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.OngoingUserCreation, res: true },
{ loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.OngoingWorkspaceActivation, res: true },
{ loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.OngoingProfileCreation, res: true },
{ loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.OngoingSyncEmail, res: true },
{ loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.Completed, res: false },
{ loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.CompletedWithoutSubscription, res: false },
@ -196,6 +222,7 @@ const testCases = [
{ loc: AppPath.Impersonate, status: OnboardingStatus.OngoingUserCreation, res: true },
{ loc: AppPath.Impersonate, status: OnboardingStatus.OngoingWorkspaceActivation, res: true },
{ loc: AppPath.Impersonate, status: OnboardingStatus.OngoingProfileCreation, res: true },
{ loc: AppPath.Impersonate, status: OnboardingStatus.OngoingSyncEmail, res: true },
{ loc: AppPath.Impersonate, status: OnboardingStatus.Completed, res: false },
{ loc: AppPath.Impersonate, status: OnboardingStatus.CompletedWithoutSubscription, res: false },
@ -206,6 +233,7 @@ const testCases = [
{ loc: AppPath.Authorize, status: OnboardingStatus.OngoingUserCreation, res: true },
{ loc: AppPath.Authorize, status: OnboardingStatus.OngoingWorkspaceActivation, res: true },
{ loc: AppPath.Authorize, status: OnboardingStatus.OngoingProfileCreation, res: true },
{ loc: AppPath.Authorize, status: OnboardingStatus.OngoingSyncEmail, res: true },
{ loc: AppPath.Authorize, status: OnboardingStatus.Completed, res: false },
{ loc: AppPath.Authorize, status: OnboardingStatus.CompletedWithoutSubscription, res: false },
@ -216,6 +244,7 @@ const testCases = [
{ loc: AppPath.NotFoundWildcard, status: OnboardingStatus.OngoingUserCreation, res: true },
{ loc: AppPath.NotFoundWildcard, status: OnboardingStatus.OngoingWorkspaceActivation, res: true },
{ loc: AppPath.NotFoundWildcard, status: OnboardingStatus.OngoingProfileCreation, res: true },
{ loc: AppPath.NotFoundWildcard, status: OnboardingStatus.OngoingSyncEmail, res: true },
{ loc: AppPath.NotFoundWildcard, status: OnboardingStatus.Completed, res: false },
{ loc: AppPath.NotFoundWildcard, status: OnboardingStatus.CompletedWithoutSubscription, res: false },
@ -226,6 +255,7 @@ const testCases = [
{ loc: AppPath.NotFound, status: OnboardingStatus.OngoingUserCreation, res: true },
{ loc: AppPath.NotFound, status: OnboardingStatus.OngoingWorkspaceActivation, res: true },
{ loc: AppPath.NotFound, status: OnboardingStatus.OngoingProfileCreation, res: true },
{ loc: AppPath.NotFound, status: OnboardingStatus.OngoingSyncEmail, res: true },
{ loc: AppPath.NotFound, status: OnboardingStatus.Completed, res: false },
{ loc: AppPath.NotFound, status: OnboardingStatus.CompletedWithoutSubscription, res: false },
];

View File

@ -27,7 +27,8 @@ export const useShowAuthModal = () => {
OnboardingStatus.Incomplete === onboardingStatus ||
OnboardingStatus.OngoingUserCreation === onboardingStatus ||
OnboardingStatus.OngoingProfileCreation === onboardingStatus ||
OnboardingStatus.OngoingWorkspaceActivation === onboardingStatus
OnboardingStatus.OngoingWorkspaceActivation === onboardingStatus ||
OnboardingStatus.OngoingSyncEmail === onboardingStatus
) {
return true;
}

View File

@ -1,5 +1,4 @@
import React, { useEffect, useState } from 'react';
import { useQuery } from '@apollo/client';
import { useRecoilState, useSetRecoilState } from 'recoil';
import { currentUserState } from '@/auth/states/currentUserState';
@ -7,9 +6,8 @@ import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMembe
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { isCurrentUserLoadedState } from '@/auth/states/isCurrentUserLoadingState';
import { workspacesState } from '@/auth/states/workspaces';
import { GET_CURRENT_USER } from '@/users/graphql/queries/getCurrentUser';
import { ColorScheme } from '@/workspace-member/types/WorkspaceMember';
import { User } from '~/generated/graphql';
import { useGetCurrentUserQuery } from '~/generated/graphql';
import { isDefined } from '~/utils/isDefined';
export const UserProviderEffect = () => {
@ -26,9 +24,7 @@ export const UserProviderEffect = () => {
currentWorkspaceMemberState,
);
const { loading: queryLoading, data: queryData } = useQuery<{
currentUser: User;
}>(GET_CURRENT_USER, {
const { loading: queryLoading, data: queryData } = useGetCurrentUserQuery({
skip: isCurrentUserLoaded,
});

View File

@ -8,6 +8,9 @@ export const USER_QUERY_FRAGMENT = gql`
email
canImpersonate
supportUserHash
state {
skipSyncEmailOnboardingStep
}
workspaceMember {
id
name {

View File

@ -4,52 +4,7 @@ import { gql } from '@apollo/client';
export const GET_CURRENT_USER = gql`
query GetCurrentUser {
currentUser {
id
firstName
lastName
email
canImpersonate
supportUserHash
workspaceMember {
id
name {
firstName
lastName
}
colorScheme
avatarUrl
locale
}
defaultWorkspace {
id
displayName
logo
domainName
inviteHash
allowImpersonation
subscriptionStatus
activationStatus
featureFlags {
id
key
value
workspaceId
}
currentCacheVersion
currentBillingSubscription {
id
status
interval
}
}
workspaces {
workspace {
id
displayName
logo
domainName
}
}
...UserQueryFragment
}
}
`;