fix onboarding for messaging (#16729)

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
neo773
2026-01-10 01:47:04 +05:30
committed by GitHub
parent f3d89ca9b9
commit 9b6eb8b80a
24 changed files with 114 additions and 15 deletions
@@ -42,7 +42,6 @@ test('Sign up with invite link via email', async ({
await loginPage.typeFirstName(firstName);
await loginPage.typeLastName(lastName);
await loginPage.clickContinueButton();
await loginPage.noSyncWithGoogle();
});
await test.step('Delete account from workspace', async () => {
@@ -63,6 +63,15 @@ describe('useSetNextOnboardingStatus', () => {
expect(nextOnboardingStatus).toEqual(OnboardingStatus.SYNC_EMAIL);
});
it('should skip SyncEmail when user is not first workspace member', () => {
const nextOnboardingStatus = renderHooks(
OnboardingStatus.PROFILE_CREATION,
false,
false,
);
expect(nextOnboardingStatus).toEqual(OnboardingStatus.COMPLETED);
});
it('should set next onboarding status for SyncEmail', () => {
const nextOnboardingStatus = renderHooks(
OnboardingStatus.SYNC_EMAIL,
@@ -22,7 +22,10 @@ const getNextOnboardingStatus = (
}
if (currentUser?.onboardingStatus === OnboardingStatus.PROFILE_CREATION) {
return OnboardingStatus.SYNC_EMAIL;
if (currentWorkspace?.workspaceMembersCount === 1) {
return OnboardingStatus.SYNC_EMAIL;
}
return OnboardingStatus.COMPLETED;
}
if (
currentUser?.onboardingStatus === OnboardingStatus.SYNC_EMAIL &&
@@ -221,6 +221,34 @@ describe('useTriggerProviderReconnect', () => {
);
expect(mockNavigate).not.toHaveBeenCalled();
});
it('should pass skipMessageChannelConfiguration option to OAuth providers', async () => {
const { result } = renderHook(() => useTriggerProviderReconnect(), {
wrapper: Wrapper,
});
const options = {
skipMessageChannelConfiguration: true,
messageVisibility: MessageChannelVisibility.SHARE_EVERYTHING,
};
await act(async () => {
await result.current.triggerProviderReconnect(
ConnectedAccountProvider.GOOGLE,
undefined,
options,
);
});
expect(mockTriggerApisOAuth).toHaveBeenCalledWith(
ConnectedAccountProvider.GOOGLE,
{
...options,
redirectLocation: '/settings/accounts',
},
);
expect(mockNavigate).not.toHaveBeenCalled();
});
});
describe('error handling', () => {
@@ -36,11 +36,13 @@ export const useTriggerApisOAuth = () => {
messageVisibility,
calendarVisibility,
loginHint,
skipMessageChannelConfiguration,
}: {
redirectLocation?: AppPath | string;
messageVisibility?: MessageChannelVisibility;
calendarVisibility?: CalendarChannelVisibility;
loginHint?: string;
skipMessageChannelConfiguration?: boolean;
} = {},
) => {
const authServerUrl = REACT_APP_SERVER_BASE_URL;
@@ -66,6 +68,10 @@ export const useTriggerApisOAuth = () => {
params += loginHint ? `&loginHint=${loginHint}` : '';
params += skipMessageChannelConfiguration
? `&skipMessageChannelConfiguration=${skipMessageChannelConfiguration}`
: '';
redirect(`${authServerUrl}/auth/${getProviderUrl(provider)}?${params}`);
},
[generateTransientToken, redirect],
@@ -69,6 +69,7 @@ export const SyncEmails = () => {
redirectLocation: AppPath.Index,
messageVisibility: visibility,
calendarVisibility: calendarChannelVisibility,
skipMessageChannelConfiguration: true,
});
};
@@ -79,6 +79,7 @@ export class GoogleAPIsAuthController {
redirectLocation,
calendarVisibility,
messageVisibility,
skipMessageChannelConfiguration,
} = user;
const { workspaceMemberId, userId, workspaceId } =
@@ -106,6 +107,7 @@ export class GoogleAPIsAuthController {
refreshToken,
calendarVisibility,
messageVisibility,
skipMessageChannelConfiguration,
});
if (userId) {
@@ -79,6 +79,7 @@ export class MicrosoftAPIsAuthController {
redirectLocation,
calendarVisibility,
messageVisibility,
skipMessageChannelConfiguration,
} = user;
const { workspaceMemberId, userId, workspaceId } =
@@ -113,6 +114,7 @@ export class MicrosoftAPIsAuthController {
refreshToken,
calendarVisibility,
messageVisibility,
skipMessageChannelConfiguration,
});
if (userId) {
@@ -53,6 +53,7 @@ export class GoogleAPIsOauthExchangeCodeForTokenGuard extends AuthGuard(
redirectLocation: state.redirectLocation,
calendarVisibility: state.calendarVisibility,
messageVisibility: state.messageVisibility,
skipMessageChannelConfiguration: state.skipMessageChannelConfiguration,
});
return (await super.canActivate(context)) as boolean;
@@ -51,6 +51,8 @@ export class GoogleAPIsOauthRequestCodeGuard extends AuthGuard('google-apis') {
redirectLocation: request.query.redirectLocation,
calendarVisibility: request.query.calendarVisibility,
messageVisibility: request.query.messageVisibility,
skipMessageChannelConfiguration:
request.query.skipMessageChannelConfiguration,
loginHint: request.query.loginHint,
userId: userId,
workspaceId: workspaceId,
@@ -45,6 +45,7 @@ export class MicrosoftAPIsOauthExchangeCodeForTokenGuard extends AuthGuard(
redirectLocation: state.redirectLocation,
calendarVisibility: state.calendarVisibility,
messageVisibility: state.messageVisibility,
skipMessageChannelConfiguration: state.skipMessageChannelConfiguration,
});
return (await super.canActivate(context)) as boolean;
@@ -64,6 +64,8 @@ export class MicrosoftAPIsOauthRequestCodeGuard extends AuthGuard(
redirectLocation: request.query.redirectLocation,
calendarVisibility: request.query.calendarVisibility,
messageVisibility: request.query.messageVisibility,
skipMessageChannelConfiguration:
request.query.skipMessageChannelConfiguration,
loginHint: request.query.loginHint,
});
@@ -18,6 +18,7 @@ export type CreateCalendarChannelInput = {
handle: string;
calendarVisibility?: CalendarChannelVisibility;
manager: WorkspaceEntityManager;
skipMessageChannelConfiguration?: boolean;
};
@Injectable()
@@ -35,6 +36,7 @@ export class CreateCalendarChannelService {
handle,
calendarVisibility,
manager,
skipMessageChannelConfiguration,
} = input;
const authContext = buildSystemAuthContext(workspaceId);
@@ -55,8 +57,12 @@ export class CreateCalendarChannelService {
handle,
visibility:
calendarVisibility || CalendarChannelVisibility.SHARE_EVERYTHING,
syncStatus: CalendarChannelSyncStatus.NOT_SYNCED,
syncStage: CalendarChannelSyncStage.PENDING_CONFIGURATION,
syncStatus: skipMessageChannelConfiguration
? CalendarChannelSyncStatus.ONGOING
: CalendarChannelSyncStatus.NOT_SYNCED,
syncStage: skipMessageChannelConfiguration
? CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_PENDING
: CalendarChannelSyncStage.PENDING_CONFIGURATION,
},
{},
manager,
@@ -22,6 +22,7 @@ export type CreateMessageChannelInput = {
handle: string;
messageVisibility?: MessageChannelVisibility;
manager: WorkspaceEntityManager;
skipMessageChannelConfiguration?: boolean;
};
@Injectable()
@@ -40,6 +41,7 @@ export class CreateMessageChannelService {
handle,
messageVisibility,
manager,
skipMessageChannelConfiguration,
} = input;
const authContext = buildSystemAuthContext(workspaceId);
@@ -63,8 +65,12 @@ export class CreateMessageChannelService {
handle,
visibility:
messageVisibility || MessageChannelVisibility.SHARE_EVERYTHING,
syncStatus: MessageChannelSyncStatus.NOT_SYNCED,
syncStage: MessageChannelSyncStage.PENDING_CONFIGURATION,
syncStatus: skipMessageChannelConfiguration
? MessageChannelSyncStatus.ONGOING
: MessageChannelSyncStatus.NOT_SYNCED,
syncStage: skipMessageChannelConfiguration
? MessageChannelSyncStage.MESSAGE_LIST_FETCH_PENDING
: MessageChannelSyncStage.PENDING_CONFIGURATION,
pendingGroupEmailsAction:
MessageChannelPendingGroupEmailsAction.NONE,
},
@@ -70,6 +70,7 @@ export class GoogleAPIsService {
refreshToken: string;
calendarVisibility: CalendarChannelVisibility | undefined;
messageVisibility: MessageChannelVisibility | undefined;
skipMessageChannelConfiguration?: boolean;
}): Promise<string> {
const {
handle,
@@ -77,6 +78,7 @@ export class GoogleAPIsService {
workspaceMemberId,
calendarVisibility,
messageVisibility,
skipMessageChannelConfiguration,
} = input;
const isCalendarEnabled = this.twentyConfigService.get(
@@ -149,6 +151,7 @@ export class GoogleAPIsService {
handle,
messageVisibility,
manager,
skipMessageChannelConfiguration,
});
if (isCalendarEnabled) {
@@ -158,6 +161,7 @@ export class GoogleAPIsService {
handle,
calendarVisibility,
manager,
skipMessageChannelConfiguration,
});
}
} else {
@@ -65,6 +65,7 @@ export class MicrosoftAPIsService {
refreshToken: string;
calendarVisibility: CalendarChannelVisibility | undefined;
messageVisibility: MessageChannelVisibility | undefined;
skipMessageChannelConfiguration?: boolean;
}): Promise<string> {
const {
handle,
@@ -72,6 +73,7 @@ export class MicrosoftAPIsService {
workspaceMemberId,
calendarVisibility,
messageVisibility,
skipMessageChannelConfiguration,
} = input;
const scopes = getMicrosoftApisOauthScopes();
@@ -130,6 +132,7 @@ export class MicrosoftAPIsService {
handle,
messageVisibility,
manager,
skipMessageChannelConfiguration,
});
if (
@@ -143,6 +146,7 @@ export class MicrosoftAPIsService {
handle,
calendarVisibility,
manager,
skipMessageChannelConfiguration,
});
}
} else {
@@ -272,6 +272,7 @@ export class SignInUpService {
await this.activateOnboardingForUser({
user,
workspace: params.workspace,
shouldShowConnectAccountStep: false,
});
await this.userWorkspaceService.addUserToWorkspaceIfUserNotInWorkspace(
@@ -301,20 +302,24 @@ export class SignInUpService {
{
user,
workspace,
shouldShowConnectAccountStep,
}: {
user: UserEntity;
workspace: WorkspaceEntity;
shouldShowConnectAccountStep: boolean;
},
queryRunner?: QueryRunner,
) {
await this.onboardingService.setOnboardingConnectAccountPending(
{
userId: user.id,
workspaceId: workspace.id,
value: true,
},
queryRunner,
);
if (shouldShowConnectAccountStep) {
await this.onboardingService.setOnboardingConnectAccountPending(
{
userId: user.id,
workspaceId: workspace.id,
value: true,
},
queryRunner,
);
}
if (user.firstName === '' && user.lastName === '') {
await this.onboardingService.setOnboardingCreateProfilePending(
@@ -515,7 +520,10 @@ export class SignInUpService {
queryRunner,
);
await this.activateOnboardingForUser({ user, workspace }, queryRunner);
await this.activateOnboardingForUser(
{ user, workspace, shouldShowConnectAccountStep: true },
queryRunner,
);
await this.onboardingService.setOnboardingInviteTeamPending(
{
@@ -42,6 +42,7 @@ export class GoogleAPIsOauthExchangeCodeForTokenStrategy extends GoogleAPIsOauth
redirectLocation: state.redirectLocation,
calendarVisibility: state.calendarVisibility,
messageVisibility: state.messageVisibility,
skipMessageChannelConfiguration: state.skipMessageChannelConfiguration,
};
done(null, user);
@@ -28,6 +28,8 @@ export class GoogleAPIsOauthRequestCodeStrategy extends GoogleAPIsOauthCommonStr
redirectLocation: req.params.redirectLocation,
calendarVisibility: req.params.calendarVisibility,
messageVisibility: req.params.messageVisibility,
skipMessageChannelConfiguration:
req.params.skipMessageChannelConfiguration,
}),
};
@@ -42,6 +42,7 @@ export class MicrosoftAPIsOauthExchangeCodeForTokenStrategy extends MicrosoftAPI
redirectLocation: state.redirectLocation,
calendarVisibility: state.calendarVisibility,
messageVisibility: state.messageVisibility,
skipMessageChannelConfiguration: state.skipMessageChannelConfiguration,
};
done(null, user);
@@ -23,6 +23,8 @@ export class MicrosoftAPIsOauthRequestCodeStrategy extends MicrosoftAPIsOauthCom
redirectLocation: req.params.redirectLocation,
calendarVisibility: req.params.calendarVisibility,
messageVisibility: req.params.messageVisibility,
skipMessageChannelConfiguration:
req.params.skipMessageChannelConfiguration,
}),
};
@@ -19,5 +19,6 @@ export type GoogleAPIsRequest = Omit<
redirectLocation?: string;
calendarVisibility?: CalendarChannelVisibility;
messageVisibility?: MessageChannelVisibility;
skipMessageChannelConfiguration?: boolean;
};
};
@@ -19,5 +19,6 @@ export type MicrosoftAPIsRequest = Omit<
redirectLocation?: string;
calendarVisibility?: CalendarChannelVisibility;
messageVisibility?: MessageChannelVisibility;
skipMessageChannelConfiguration?: boolean;
};
};
@@ -12,6 +12,7 @@ type GoogleAPIsRequestExtraParams = {
loginHint?: string;
userId?: string;
workspaceId?: string;
skipMessageChannelConfiguration?: string;
};
export const setRequestExtraParams = (
@@ -26,6 +27,7 @@ export const setRequestExtraParams = (
loginHint,
userId,
workspaceId,
skipMessageChannelConfiguration,
} = params;
if (!transientToken) {
@@ -60,4 +62,9 @@ export const setRequestExtraParams = (
if (workspaceId) {
request.params.workspaceId = workspaceId;
}
if (skipMessageChannelConfiguration) {
request.params.skipMessageChannelConfiguration =
skipMessageChannelConfiguration;
}
};