Message channel change 1 (#14942)
This commit is contained in:
@@ -483,6 +483,11 @@ export enum CaptchaDriverType {
|
||||
TURNSTILE = 'TURNSTILE'
|
||||
}
|
||||
|
||||
export type ChannelSyncSuccess = {
|
||||
__typename?: 'ChannelSyncSuccess';
|
||||
success: Scalars['Boolean'];
|
||||
};
|
||||
|
||||
export type CheckUserExistOutput = {
|
||||
__typename?: 'CheckUserExistOutput';
|
||||
availableWorkspacesCount: Scalars['Float'];
|
||||
@@ -1483,6 +1488,7 @@ export type ImapSmtpCaldavConnectionParameters = {
|
||||
|
||||
export type ImapSmtpCaldavConnectionSuccess = {
|
||||
__typename?: 'ImapSmtpCaldavConnectionSuccess';
|
||||
connectedAccountId: Scalars['String'];
|
||||
success: Scalars['Boolean'];
|
||||
};
|
||||
|
||||
@@ -1787,6 +1793,7 @@ export type Mutation = {
|
||||
signUpInWorkspace: SignUpOutput;
|
||||
skipBookOnboardingStep: OnboardingStepSuccess;
|
||||
skipSyncEmailOnboardingStep: OnboardingStepSuccess;
|
||||
startChannelSync: ChannelSyncSuccess;
|
||||
submitFormStep: Scalars['Boolean'];
|
||||
switchBillingPlan: BillingUpdateOutput;
|
||||
switchSubscriptionInterval: BillingUpdateOutput;
|
||||
@@ -2454,6 +2461,11 @@ export type MutationSignUpInWorkspaceArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type MutationStartChannelSyncArgs = {
|
||||
connectedAccountId: Scalars['UUID'];
|
||||
};
|
||||
|
||||
|
||||
export type MutationSubmitFormStepArgs = {
|
||||
input: SubmitFormStepInput;
|
||||
};
|
||||
@@ -5156,7 +5168,14 @@ export type SaveImapSmtpCaldavAccountMutationVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type SaveImapSmtpCaldavAccountMutation = { __typename?: 'Mutation', saveImapSmtpCaldavAccount: { __typename?: 'ImapSmtpCaldavConnectionSuccess', success: boolean } };
|
||||
export type SaveImapSmtpCaldavAccountMutation = { __typename?: 'Mutation', saveImapSmtpCaldavAccount: { __typename?: 'ImapSmtpCaldavConnectionSuccess', success: boolean, connectedAccountId: string } };
|
||||
|
||||
export type StartChannelSyncMutationVariables = Exact<{
|
||||
connectedAccountId: Scalars['UUID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type StartChannelSyncMutation = { __typename?: 'Mutation', startChannelSync: { __typename?: 'ChannelSyncSuccess', success: boolean } };
|
||||
|
||||
export type GetConnectedImapSmtpCaldavAccountQueryVariables = Exact<{
|
||||
id: Scalars['UUID'];
|
||||
@@ -9442,6 +9461,7 @@ export const SaveImapSmtpCaldavAccountDocument = gql`
|
||||
id: $id
|
||||
) {
|
||||
success
|
||||
connectedAccountId
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -9474,6 +9494,39 @@ export function useSaveImapSmtpCaldavAccountMutation(baseOptions?: Apollo.Mutati
|
||||
export type SaveImapSmtpCaldavAccountMutationHookResult = ReturnType<typeof useSaveImapSmtpCaldavAccountMutation>;
|
||||
export type SaveImapSmtpCaldavAccountMutationResult = Apollo.MutationResult<SaveImapSmtpCaldavAccountMutation>;
|
||||
export type SaveImapSmtpCaldavAccountMutationOptions = Apollo.BaseMutationOptions<SaveImapSmtpCaldavAccountMutation, SaveImapSmtpCaldavAccountMutationVariables>;
|
||||
export const StartChannelSyncDocument = gql`
|
||||
mutation StartChannelSync($connectedAccountId: UUID!) {
|
||||
startChannelSync(connectedAccountId: $connectedAccountId) {
|
||||
success
|
||||
}
|
||||
}
|
||||
`;
|
||||
export type StartChannelSyncMutationFn = Apollo.MutationFunction<StartChannelSyncMutation, StartChannelSyncMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useStartChannelSyncMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useStartChannelSyncMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useStartChannelSyncMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [startChannelSyncMutation, { data, loading, error }] = useStartChannelSyncMutation({
|
||||
* variables: {
|
||||
* connectedAccountId: // value for 'connectedAccountId'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useStartChannelSyncMutation(baseOptions?: Apollo.MutationHookOptions<StartChannelSyncMutation, StartChannelSyncMutationVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useMutation<StartChannelSyncMutation, StartChannelSyncMutationVariables>(StartChannelSyncDocument, options);
|
||||
}
|
||||
export type StartChannelSyncMutationHookResult = ReturnType<typeof useStartChannelSyncMutation>;
|
||||
export type StartChannelSyncMutationResult = Apollo.MutationResult<StartChannelSyncMutation>;
|
||||
export type StartChannelSyncMutationOptions = Apollo.BaseMutationOptions<StartChannelSyncMutation, StartChannelSyncMutationVariables>;
|
||||
export const GetConnectedImapSmtpCaldavAccountDocument = gql`
|
||||
query GetConnectedImapSmtpCaldavAccount($id: UUID!) {
|
||||
getConnectedImapSmtpCaldavAccount(id: $id) {
|
||||
|
||||
@@ -483,6 +483,11 @@ export enum CaptchaDriverType {
|
||||
TURNSTILE = 'TURNSTILE'
|
||||
}
|
||||
|
||||
export type ChannelSyncSuccess = {
|
||||
__typename?: 'ChannelSyncSuccess';
|
||||
success: Scalars['Boolean'];
|
||||
};
|
||||
|
||||
export type CheckUserExistOutput = {
|
||||
__typename?: 'CheckUserExistOutput';
|
||||
availableWorkspacesCount: Scalars['Float'];
|
||||
@@ -1440,6 +1445,7 @@ export type ImapSmtpCaldavConnectionParameters = {
|
||||
|
||||
export type ImapSmtpCaldavConnectionSuccess = {
|
||||
__typename?: 'ImapSmtpCaldavConnectionSuccess';
|
||||
connectedAccountId: Scalars['String'];
|
||||
success: Scalars['Boolean'];
|
||||
};
|
||||
|
||||
@@ -1742,6 +1748,7 @@ export type Mutation = {
|
||||
signUpInWorkspace: SignUpOutput;
|
||||
skipBookOnboardingStep: OnboardingStepSuccess;
|
||||
skipSyncEmailOnboardingStep: OnboardingStepSuccess;
|
||||
startChannelSync: ChannelSyncSuccess;
|
||||
submitFormStep: Scalars['Boolean'];
|
||||
switchBillingPlan: BillingUpdateOutput;
|
||||
switchSubscriptionInterval: BillingUpdateOutput;
|
||||
@@ -2385,6 +2392,11 @@ export type MutationSignUpInWorkspaceArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type MutationStartChannelSyncArgs = {
|
||||
connectedAccountId: Scalars['UUID'];
|
||||
};
|
||||
|
||||
|
||||
export type MutationSubmitFormStepArgs = {
|
||||
input: SubmitFormStepInput;
|
||||
};
|
||||
|
||||
@@ -9,6 +9,7 @@ export enum CalendarChannelSyncStatus {
|
||||
}
|
||||
|
||||
export enum CalendarChannelSyncStage {
|
||||
PENDING_CONFIGURATION = 'PENDING_CONFIGURATION',
|
||||
FULL_CALENDAR_EVENT_LIST_FETCH_PENDING = 'FULL_CALENDAR_EVENT_LIST_FETCH_PENDING', // WILL BE DEPRECATED
|
||||
PARTIAL_CALENDAR_EVENT_LIST_FETCH_PENDING = 'PARTIAL_CALENDAR_EVENT_LIST_FETCH_PENDING', // DEPRECATED
|
||||
CALENDAR_EVENT_LIST_FETCH_PENDING = 'CALENDAR_EVENT_LIST_FETCH_PENDING',
|
||||
|
||||
@@ -23,6 +23,7 @@ export enum MessageChannelSyncStatus {
|
||||
}
|
||||
|
||||
export enum MessageChannelSyncStage {
|
||||
PENDING_CONFIGURATION = 'PENDING_CONFIGURATION',
|
||||
FULL_MESSAGE_LIST_FETCH_PENDING = 'FULL_MESSAGE_LIST_FETCH_PENDING', // WILL BE DEPRECATED
|
||||
PARTIAL_MESSAGE_LIST_FETCH_PENDING = 'PARTIAL_MESSAGE_LIST_FETCH_PENDING', // DEPRECATED
|
||||
MESSAGE_LIST_FETCH_PENDING = 'MESSAGE_LIST_FETCH_PENDING',
|
||||
|
||||
@@ -37,6 +37,14 @@ const SettingsAccountsEmails = lazy(() =>
|
||||
})),
|
||||
);
|
||||
|
||||
const SettingsAccountsConfiguration = lazy(() =>
|
||||
import('~/pages/settings/accounts/SettingsAccountsConfiguration').then(
|
||||
(module) => ({
|
||||
default: module.SettingsAccountsConfiguration,
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
const SettingsNewAccount = lazy(() =>
|
||||
import('~/pages/settings/accounts/SettingsNewAccount').then((module) => ({
|
||||
default: module.SettingsNewAccount,
|
||||
@@ -405,6 +413,10 @@ export const SettingsRoutes = ({
|
||||
<Route path={SettingsPath.Experience} element={<SettingsExperience />} />
|
||||
<Route path={SettingsPath.Accounts} element={<SettingsAccounts />} />
|
||||
<Route path={SettingsPath.NewAccount} element={<SettingsNewAccount />} />
|
||||
<Route
|
||||
path={SettingsPath.AccountsConfiguration}
|
||||
element={<SettingsAccountsConfiguration />}
|
||||
/>
|
||||
<Route
|
||||
path={SettingsPath.AccountsCalendars}
|
||||
element={<SettingsAccountsCalendars />}
|
||||
|
||||
+7
-1
@@ -1,7 +1,10 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { type CalendarChannel } from '@/accounts/types/CalendarChannel';
|
||||
import {
|
||||
type CalendarChannel,
|
||||
CalendarChannelSyncStage,
|
||||
} from '@/accounts/types/CalendarChannel';
|
||||
import { type ConnectedAccount } from '@/accounts/types/ConnectedAccount';
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
@@ -44,6 +47,9 @@ export const SettingsAccountsCalendarChannelsContainer = () => {
|
||||
connectedAccountId: {
|
||||
in: accounts.map((account) => account.id),
|
||||
},
|
||||
syncStage: {
|
||||
neq: CalendarChannelSyncStage.PENDING_CONFIGURATION,
|
||||
},
|
||||
},
|
||||
skip: !accounts.length,
|
||||
});
|
||||
|
||||
+7
-1
@@ -2,7 +2,10 @@ import styled from '@emotion/styled';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { type ConnectedAccount } from '@/accounts/types/ConnectedAccount';
|
||||
import { type MessageChannel } from '@/accounts/types/MessageChannel';
|
||||
import {
|
||||
type MessageChannel,
|
||||
MessageChannelSyncStage,
|
||||
} from '@/accounts/types/MessageChannel';
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useGenerateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/record-gql-fields/hooks/useGenerateDepthRecordGqlFieldsFromObject';
|
||||
@@ -58,6 +61,9 @@ export const SettingsAccountsMessageChannelsContainer = () => {
|
||||
isSyncEnabled: {
|
||||
eq: true,
|
||||
},
|
||||
syncStage: {
|
||||
neq: MessageChannelSyncStage.PENDING_CONFIGURATION,
|
||||
},
|
||||
},
|
||||
recordGqlFields,
|
||||
onCompleted: (data) => {
|
||||
|
||||
+1
@@ -14,6 +14,7 @@ export const SAVE_IMAP_SMTP_CALDAV_ACCOUNT = gql`
|
||||
id: $id
|
||||
) {
|
||||
success
|
||||
connectedAccountId
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
export const START_CHANNEL_SYNC = gql`
|
||||
mutation StartChannelSync($connectedAccountId: UUID!) {
|
||||
startChannelSync(connectedAccountId: $connectedAccountId) {
|
||||
success
|
||||
}
|
||||
}
|
||||
`;
|
||||
+9
-2
@@ -132,7 +132,7 @@ export const useImapSmtpCaldavConnectionForm = ({
|
||||
});
|
||||
|
||||
try {
|
||||
await saveConnection({
|
||||
const { data } = await saveConnection({
|
||||
variables: {
|
||||
...(isEditing && connectedAccountId
|
||||
? { id: connectedAccountId }
|
||||
@@ -142,13 +142,20 @@ export const useImapSmtpCaldavConnectionForm = ({
|
||||
connectionParameters,
|
||||
},
|
||||
});
|
||||
if (!isDefined(data)) return;
|
||||
|
||||
const successMessage = isEditing
|
||||
? t`Connection successfully updated`
|
||||
: t`Connection successfully created`;
|
||||
|
||||
enqueueSuccessSnackBar({ message: successMessage });
|
||||
navigate(SettingsPath.Accounts);
|
||||
|
||||
const { connectedAccountId: returnedConnectedAccountId } =
|
||||
data?.saveImapSmtpCaldavAccount || {};
|
||||
|
||||
navigate(SettingsPath.AccountsConfiguration, {
|
||||
connectedAccountId: returnedConnectedAccountId,
|
||||
});
|
||||
} catch (error) {
|
||||
enqueueErrorSnackBar({
|
||||
apolloError: error instanceof ApolloError ? error : undefined,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { type ConnectedAccount } from '@/accounts/types/ConnectedAccount';
|
||||
import { MessageChannelSyncStage } from '@/accounts/types/MessageChannel';
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useGenerateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/record-gql-fields/hooks/useGenerateDepthRecordGqlFieldsFromObject';
|
||||
@@ -26,14 +27,25 @@ export const SettingsAccounts = () => {
|
||||
shouldOnlyLoadRelationIdentifiers: false,
|
||||
});
|
||||
|
||||
const { records: accounts, loading } = useFindManyRecords<ConnectedAccount>({
|
||||
objectNameSingular: CoreObjectNameSingular.ConnectedAccount,
|
||||
filter: {
|
||||
accountOwnerId: {
|
||||
eq: currentWorkspaceMember?.id,
|
||||
const { records: allAccounts, loading } =
|
||||
useFindManyRecords<ConnectedAccount>({
|
||||
objectNameSingular: CoreObjectNameSingular.ConnectedAccount,
|
||||
filter: {
|
||||
accountOwnerId: {
|
||||
eq: currentWorkspaceMember?.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
recordGqlFields,
|
||||
recordGqlFields,
|
||||
});
|
||||
|
||||
const accountsToShow = allAccounts.filter((account) => {
|
||||
return (
|
||||
account.messageChannels.length === 0 ||
|
||||
account.messageChannels.some(
|
||||
(channel) =>
|
||||
channel.syncStage !== MessageChannelSyncStage.PENDING_CONFIGURATION,
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
@@ -57,7 +69,9 @@ export const SettingsAccounts = () => {
|
||||
title={t`Connected accounts`}
|
||||
description={t`Manage your internet accounts.`}
|
||||
/>
|
||||
<SettingsAccountsConnectedAccountsListCard accounts={accounts} />
|
||||
<SettingsAccountsConnectedAccountsListCard
|
||||
accounts={accountsToShow}
|
||||
/>
|
||||
</Section>
|
||||
<SettingsAccountsBlocklistSection />
|
||||
<SettingsAccountsSettingsSection />
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useState } from 'react';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
|
||||
import { type CalendarChannel } from '@/accounts/types/CalendarChannel';
|
||||
import { type MessageChannel } from '@/accounts/types/MessageChannel';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { SettingsPath } from 'twenty-shared/types';
|
||||
import { getSettingsPath, isDefined } from 'twenty-shared/utils';
|
||||
import { useStartChannelSyncMutation } from '~/generated-metadata/graphql';
|
||||
import { SettingsAccountsConfigurationStepCalendar } from '~/pages/settings/accounts/SettingsAccountsConfigurationStepCalendar';
|
||||
import { SettingsAccountsConfigurationStepEmail } from '~/pages/settings/accounts/SettingsAccountsConfigurationStepEmail';
|
||||
|
||||
enum SettingsAccountsConfigurationStep {
|
||||
Email = 'email',
|
||||
Calendar = 'calendar',
|
||||
}
|
||||
|
||||
export const SettingsAccountsConfiguration = () => {
|
||||
const { t } = useLingui();
|
||||
const { connectedAccountId } = useParams<{
|
||||
connectedAccountId: string;
|
||||
}>();
|
||||
const navigate = useNavigate();
|
||||
const { enqueueSuccessSnackBar, enqueueErrorSnackBar } = useSnackBar();
|
||||
const [startChannelSyncMutation, { loading: isSubmitting }] =
|
||||
useStartChannelSyncMutation();
|
||||
|
||||
const [currentStep, setCurrentStep] =
|
||||
useState<SettingsAccountsConfigurationStep>(
|
||||
SettingsAccountsConfigurationStep.Email,
|
||||
);
|
||||
|
||||
const { records: messageChannels } = useFindManyRecords<MessageChannel>({
|
||||
objectNameSingular: CoreObjectNameSingular.MessageChannel,
|
||||
filter: {
|
||||
connectedAccountId: {
|
||||
eq: connectedAccountId,
|
||||
},
|
||||
},
|
||||
skip: !connectedAccountId,
|
||||
});
|
||||
|
||||
const { records: calendarChannels } = useFindManyRecords<CalendarChannel>({
|
||||
objectNameSingular: CoreObjectNameSingular.CalendarChannel,
|
||||
filter: {
|
||||
connectedAccountId: {
|
||||
eq: connectedAccountId,
|
||||
},
|
||||
},
|
||||
skip: !connectedAccountId,
|
||||
});
|
||||
|
||||
const messageChannel = messageChannels[0];
|
||||
const calendarChannel = calendarChannels[0];
|
||||
|
||||
const handleNext = () => {
|
||||
setCurrentStep(SettingsAccountsConfigurationStep.Calendar);
|
||||
};
|
||||
|
||||
const handleAddAccount = async () => {
|
||||
if (!connectedAccountId) return;
|
||||
|
||||
await startChannelSyncMutation({
|
||||
variables: {
|
||||
connectedAccountId,
|
||||
},
|
||||
onCompleted: () => {
|
||||
enqueueSuccessSnackBar({
|
||||
message: t`Account added successfully. Sync started.`,
|
||||
});
|
||||
navigate(getSettingsPath(SettingsPath.Accounts));
|
||||
},
|
||||
onError: (error) => {
|
||||
enqueueErrorSnackBar({
|
||||
apolloError: error,
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
switch (currentStep) {
|
||||
case SettingsAccountsConfigurationStep.Email:
|
||||
if (!isDefined(messageChannel)) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<SettingsAccountsConfigurationStepEmail
|
||||
messageChannel={messageChannel}
|
||||
hasNextStep={isDefined(calendarChannel)}
|
||||
isSubmitting={isSubmitting}
|
||||
onNext={handleNext}
|
||||
onAddAccount={handleAddAccount}
|
||||
/>
|
||||
);
|
||||
case SettingsAccountsConfigurationStep.Calendar:
|
||||
if (!isDefined(calendarChannel)) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<SettingsAccountsConfigurationStepCalendar
|
||||
calendarChannel={calendarChannel}
|
||||
messageChannel={messageChannel}
|
||||
isSubmitting={isSubmitting}
|
||||
onAddAccount={handleAddAccount}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
import { Trans, useLingui } from '@lingui/react/macro';
|
||||
|
||||
import { type CalendarChannel } from '@/accounts/types/CalendarChannel';
|
||||
import { type MessageChannel } from '@/accounts/types/MessageChannel';
|
||||
import { SettingsAccountsCalendarChannelDetails } from '@/settings/accounts/components/SettingsAccountsCalendarChannelDetails';
|
||||
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer';
|
||||
import { SettingsPath } from 'twenty-shared/types';
|
||||
import { getSettingsPath, isDefined } from 'twenty-shared/utils';
|
||||
import { IconPlus } from 'twenty-ui/display';
|
||||
import { Button } from 'twenty-ui/input';
|
||||
|
||||
type SettingsAccountsConfigurationStepCalendarProps = {
|
||||
calendarChannel: CalendarChannel;
|
||||
messageChannel?: MessageChannel;
|
||||
isSubmitting: boolean;
|
||||
onAddAccount: () => void;
|
||||
};
|
||||
|
||||
export const SettingsAccountsConfigurationStepCalendar = ({
|
||||
calendarChannel,
|
||||
messageChannel,
|
||||
isSubmitting,
|
||||
onAddAccount,
|
||||
}: SettingsAccountsConfigurationStepCalendarProps) => {
|
||||
const { t } = useLingui();
|
||||
|
||||
const stepNumber = isDefined(messageChannel) ? 2 : 1;
|
||||
const stepTitle = t`${stepNumber}. Calendar`;
|
||||
|
||||
return (
|
||||
<SubMenuTopBarContainer
|
||||
title={stepTitle}
|
||||
links={[
|
||||
{
|
||||
children: <Trans>User</Trans>,
|
||||
href: getSettingsPath(SettingsPath.ProfilePage),
|
||||
},
|
||||
{
|
||||
children: <Trans>Account</Trans>,
|
||||
href: getSettingsPath(SettingsPath.Accounts),
|
||||
},
|
||||
{
|
||||
children: stepTitle,
|
||||
},
|
||||
]}
|
||||
actionButton={
|
||||
<Button
|
||||
Icon={IconPlus}
|
||||
title={t`Add account`}
|
||||
accent="blue"
|
||||
size="small"
|
||||
variant="primary"
|
||||
onClick={onAddAccount}
|
||||
disabled={isSubmitting}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<SettingsPageContainer>
|
||||
<SettingsAccountsCalendarChannelDetails
|
||||
calendarChannel={calendarChannel}
|
||||
/>
|
||||
</SettingsPageContainer>
|
||||
</SubMenuTopBarContainer>
|
||||
);
|
||||
};
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
import { Trans, useLingui } from '@lingui/react/macro';
|
||||
|
||||
import { type MessageChannel } from '@/accounts/types/MessageChannel';
|
||||
import { SettingsAccountsMessageChannelDetails } from '@/settings/accounts/components/SettingsAccountsMessageChannelDetails';
|
||||
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer';
|
||||
import { SettingsPath } from 'twenty-shared/types';
|
||||
import { getSettingsPath } from 'twenty-shared/utils';
|
||||
import { IconChevronRight, IconPlus } from 'twenty-ui/display';
|
||||
import { Button } from 'twenty-ui/input';
|
||||
|
||||
type SettingsAccountsConfigurationStepEmailProps = {
|
||||
messageChannel: MessageChannel;
|
||||
hasNextStep: boolean;
|
||||
isSubmitting: boolean;
|
||||
onNext: () => void;
|
||||
onAddAccount: () => void;
|
||||
};
|
||||
|
||||
export const SettingsAccountsConfigurationStepEmail = ({
|
||||
messageChannel,
|
||||
hasNextStep,
|
||||
isSubmitting,
|
||||
onNext,
|
||||
onAddAccount,
|
||||
}: SettingsAccountsConfigurationStepEmailProps) => {
|
||||
const { t } = useLingui();
|
||||
|
||||
return (
|
||||
<SubMenuTopBarContainer
|
||||
title={t`1. Email`}
|
||||
links={[
|
||||
{
|
||||
children: <Trans>User</Trans>,
|
||||
href: getSettingsPath(SettingsPath.ProfilePage),
|
||||
},
|
||||
{
|
||||
children: <Trans>Account</Trans>,
|
||||
href: getSettingsPath(SettingsPath.Accounts),
|
||||
},
|
||||
{
|
||||
children: t`1. Email`,
|
||||
},
|
||||
]}
|
||||
actionButton={
|
||||
hasNextStep ? (
|
||||
<Button
|
||||
Icon={IconChevronRight}
|
||||
title={t`Next`}
|
||||
accent="blue"
|
||||
size="small"
|
||||
variant="secondary"
|
||||
onClick={onNext}
|
||||
disabled={isSubmitting}
|
||||
/>
|
||||
) : (
|
||||
<Button
|
||||
Icon={IconPlus}
|
||||
title={t`Add account`}
|
||||
accent="blue"
|
||||
size="small"
|
||||
variant="primary"
|
||||
onClick={onAddAccount}
|
||||
disabled={isSubmitting}
|
||||
/>
|
||||
)
|
||||
}
|
||||
>
|
||||
<SettingsPageContainer>
|
||||
<SettingsAccountsMessageChannelDetails
|
||||
messageChannel={messageChannel}
|
||||
/>
|
||||
</SettingsPageContainer>
|
||||
</SubMenuTopBarContainer>
|
||||
);
|
||||
};
|
||||
+423
@@ -0,0 +1,423 @@
|
||||
import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { Command } from 'nest-commander';
|
||||
import { DataSource, Repository } from 'typeorm';
|
||||
|
||||
import {
|
||||
ActiveOrSuspendedWorkspacesMigrationCommandRunner,
|
||||
type RunOnWorkspaceArgs,
|
||||
} from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { type FieldMetadataComplexOption } from 'src/engine/metadata-modules/field-metadata/dtos/options.input';
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';
|
||||
import { getWorkspaceSchemaName } from 'src/engine/workspace-datasource/utils/get-workspace-schema-name.util';
|
||||
import {
|
||||
CALENDAR_CHANNEL_STANDARD_FIELD_IDS,
|
||||
MESSAGE_CHANNEL_STANDARD_FIELD_IDS,
|
||||
} from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
import { CalendarChannelSyncStage } from 'src/modules/calendar/common/standard-objects/calendar-channel.workspace-entity';
|
||||
import { MessageChannelSyncStage } from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity';
|
||||
|
||||
@Command({
|
||||
name: 'upgrade:1-10:migrate-channel-sync-stages',
|
||||
description:
|
||||
'Migrate message and calendar channel sync stages: add PENDING_CONFIGURATION, add MESSAGE_LIST_FETCH_SCHEDULED, migrate deprecated stages',
|
||||
})
|
||||
export class MigrateChannelSyncStagesCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
|
||||
constructor(
|
||||
@InjectRepository(Workspace)
|
||||
protected readonly workspaceRepository: Repository<Workspace>,
|
||||
protected readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
@InjectRepository(ObjectMetadataEntity)
|
||||
private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
|
||||
@InjectRepository(FieldMetadataEntity)
|
||||
private readonly fieldMetadataRepository: Repository<FieldMetadataEntity>,
|
||||
@InjectDataSource()
|
||||
private readonly coreDataSource: DataSource,
|
||||
) {
|
||||
super(workspaceRepository, twentyORMGlobalManager);
|
||||
}
|
||||
|
||||
override async runOnWorkspace({
|
||||
workspaceId,
|
||||
options,
|
||||
}: RunOnWorkspaceArgs): Promise<void> {
|
||||
this.logger.log(
|
||||
`Migrating channel sync stages for workspace ${workspaceId}`,
|
||||
);
|
||||
|
||||
await this.updateMessageChannelSyncStageFieldMetadata(workspaceId, options);
|
||||
|
||||
await this.updateCalendarChannelSyncStageFieldMetadata(
|
||||
workspaceId,
|
||||
options,
|
||||
);
|
||||
|
||||
const schemaName = getWorkspaceSchemaName(workspaceId);
|
||||
|
||||
await this.migrateMessageChannelSyncStages(
|
||||
workspaceId,
|
||||
schemaName,
|
||||
options,
|
||||
);
|
||||
|
||||
await this.migrateCalendarChannelSyncStages(
|
||||
workspaceId,
|
||||
schemaName,
|
||||
options,
|
||||
);
|
||||
|
||||
this.logger.log(
|
||||
`Successfully migrated channel sync stages for workspace ${workspaceId}`,
|
||||
);
|
||||
}
|
||||
|
||||
private async migrateMessageChannelSyncStages(
|
||||
workspaceId: string,
|
||||
schemaName: string,
|
||||
options: RunOnWorkspaceArgs['options'],
|
||||
): Promise<void> {
|
||||
const messageChannelObject = await this.objectMetadataRepository.findOne({
|
||||
where: {
|
||||
standardId: STANDARD_OBJECT_IDS.messageChannel,
|
||||
workspaceId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!messageChannelObject) {
|
||||
this.logger.log(
|
||||
`MessageChannel object not found for workspace ${workspaceId}, skipping schema migration`,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const tableName = computeObjectTargetTable(messageChannelObject);
|
||||
|
||||
// Add new enum values for MessageChannelSyncStage
|
||||
if (options.dryRun) {
|
||||
this.logger.log(
|
||||
`Would try to add PENDING_CONFIGURATION to messageChannel_syncStage_enum for workspace ${workspaceId}`,
|
||||
);
|
||||
} else {
|
||||
try {
|
||||
await this.coreDataSource.query(
|
||||
`ALTER TYPE ${schemaName}."messageChannel_syncStage_enum" ADD VALUE IF NOT EXISTS 'PENDING_CONFIGURATION'`,
|
||||
);
|
||||
this.logger.log(
|
||||
`Added PENDING_CONFIGURATION to messageChannel_syncStage_enum for workspace ${workspaceId}`,
|
||||
);
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`Error adding PENDING_CONFIGURATION to messageChannel_syncStage_enum for workspace ${workspaceId}: ${error}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (options.dryRun) {
|
||||
this.logger.log(
|
||||
`Would try to add MESSAGE_LIST_FETCH_SCHEDULED to messageChannel_syncStage_enum for workspace ${workspaceId}`,
|
||||
);
|
||||
} else {
|
||||
try {
|
||||
await this.coreDataSource.query(
|
||||
`ALTER TYPE ${schemaName}."messageChannel_syncStage_enum" ADD VALUE IF NOT EXISTS 'MESSAGE_LIST_FETCH_SCHEDULED'`,
|
||||
);
|
||||
this.logger.log(
|
||||
`Added MESSAGE_LIST_FETCH_SCHEDULED to messageChannel_syncStage_enum for workspace ${workspaceId}`,
|
||||
);
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`Error adding MESSAGE_LIST_FETCH_SCHEDULED to messageChannel_syncStage_enum for workspace ${workspaceId}: ${error}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Migrate deprecated MessageChannel sync stages
|
||||
if (options.dryRun) {
|
||||
this.logger.log(
|
||||
`Would migrate deprecated messageChannel sync stages for workspace ${workspaceId}`,
|
||||
);
|
||||
} else {
|
||||
const messageChannelUpdateResult = await this.coreDataSource.query(
|
||||
`UPDATE "${schemaName}"."${tableName}"
|
||||
SET "syncStage" = 'MESSAGE_LIST_FETCH_PENDING'
|
||||
WHERE "syncStage" IN ('FULL_MESSAGE_LIST_FETCH_PENDING', 'PARTIAL_MESSAGE_LIST_FETCH_PENDING')`,
|
||||
);
|
||||
|
||||
const messageChannelRowsUpdated = messageChannelUpdateResult[1] || 0;
|
||||
|
||||
this.logger.log(
|
||||
`Migrated ${messageChannelRowsUpdated} messageChannel records from deprecated sync stages in workspace ${workspaceId}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private async migrateCalendarChannelSyncStages(
|
||||
workspaceId: string,
|
||||
schemaName: string,
|
||||
options: RunOnWorkspaceArgs['options'],
|
||||
): Promise<void> {
|
||||
const calendarChannelObject = await this.objectMetadataRepository.findOne({
|
||||
where: {
|
||||
standardId: STANDARD_OBJECT_IDS.calendarChannel,
|
||||
workspaceId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!calendarChannelObject) {
|
||||
this.logger.log(
|
||||
`CalendarChannel object not found for workspace ${workspaceId}, skipping schema migration`,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const tableName = computeObjectTargetTable(calendarChannelObject);
|
||||
|
||||
// Add new enum values for CalendarChannelSyncStage
|
||||
if (options.dryRun) {
|
||||
this.logger.log(
|
||||
`Would try to add PENDING_CONFIGURATION to calendarChannel_syncStage_enum for workspace ${workspaceId}`,
|
||||
);
|
||||
} else {
|
||||
try {
|
||||
await this.coreDataSource.query(
|
||||
`ALTER TYPE ${schemaName}."calendarChannel_syncStage_enum" ADD VALUE IF NOT EXISTS 'PENDING_CONFIGURATION'`,
|
||||
);
|
||||
this.logger.log(
|
||||
`Added PENDING_CONFIGURATION to calendarChannel_syncStage_enum for workspace ${workspaceId}`,
|
||||
);
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`Error adding PENDING_CONFIGURATION to calendarChannel_syncStage_enum for workspace ${workspaceId}: ${error}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (options.dryRun) {
|
||||
this.logger.log(
|
||||
`Would try to add CALENDAR_EVENT_LIST_FETCH_SCHEDULED to calendarChannel_syncStage_enum for workspace ${workspaceId}`,
|
||||
);
|
||||
} else {
|
||||
try {
|
||||
await this.coreDataSource.query(
|
||||
`ALTER TYPE ${schemaName}."calendarChannel_syncStage_enum" ADD VALUE IF NOT EXISTS 'CALENDAR_EVENT_LIST_FETCH_SCHEDULED'`,
|
||||
);
|
||||
this.logger.log(
|
||||
`Added CALENDAR_EVENT_LIST_FETCH_SCHEDULED to calendarChannel_syncStage_enum for workspace ${workspaceId}`,
|
||||
);
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`Error adding CALENDAR_EVENT_LIST_FETCH_SCHEDULED to calendarChannel_syncStage_enum for workspace ${workspaceId}: ${error}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Migrate deprecated CalendarChannel sync stages
|
||||
if (options.dryRun) {
|
||||
this.logger.log(
|
||||
`Would migrate deprecated calendarChannel sync stages for workspace ${workspaceId}`,
|
||||
);
|
||||
} else {
|
||||
const calendarChannelUpdateResult = await this.coreDataSource.query(
|
||||
`UPDATE "${schemaName}"."${tableName}"
|
||||
SET "syncStage" = 'CALENDAR_EVENT_LIST_FETCH_PENDING'
|
||||
WHERE "syncStage" IN ('FULL_CALENDAR_EVENT_LIST_FETCH_PENDING', 'PARTIAL_CALENDAR_EVENT_LIST_FETCH_PENDING')`,
|
||||
);
|
||||
|
||||
const calendarChannelRowsUpdated = calendarChannelUpdateResult[1] || 0;
|
||||
|
||||
this.logger.log(
|
||||
`Migrated ${calendarChannelRowsUpdated} calendarChannel records from deprecated sync stages in workspace ${workspaceId}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private async updateMessageChannelSyncStageFieldMetadata(
|
||||
workspaceId: string,
|
||||
options: RunOnWorkspaceArgs['options'],
|
||||
): Promise<void> {
|
||||
const messageChannelObject = await this.objectMetadataRepository.findOne({
|
||||
where: {
|
||||
standardId: STANDARD_OBJECT_IDS.messageChannel,
|
||||
workspaceId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!messageChannelObject) {
|
||||
this.logger.log(
|
||||
`MessageChannel object not found for workspace ${workspaceId}, skipping field metadata update`,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const syncStageField = await this.fieldMetadataRepository.findOne({
|
||||
where: {
|
||||
standardId: MESSAGE_CHANNEL_STANDARD_FIELD_IDS.syncStage,
|
||||
objectMetadataId: messageChannelObject.id,
|
||||
workspaceId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!syncStageField) {
|
||||
this.logger.log(
|
||||
`MessageChannel syncStage field not found for workspace ${workspaceId}, skipping field metadata update`,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const fieldOptions = (syncStageField.options ||
|
||||
[]) as FieldMetadataComplexOption[];
|
||||
|
||||
const hasMessageListFetchScheduled = fieldOptions.some(
|
||||
(option) =>
|
||||
option.value === MessageChannelSyncStage.MESSAGE_LIST_FETCH_SCHEDULED,
|
||||
);
|
||||
|
||||
const hasPendingConfiguration = fieldOptions.some(
|
||||
(option) =>
|
||||
option.value === MessageChannelSyncStage.PENDING_CONFIGURATION,
|
||||
);
|
||||
|
||||
if (hasMessageListFetchScheduled && hasPendingConfiguration) {
|
||||
this.logger.log(
|
||||
`MessageChannel syncStage field metadata already migrated for workspace ${workspaceId}`,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (options.dryRun) {
|
||||
this.logger.log(
|
||||
`Would update MessageChannel syncStage field metadata for workspace ${workspaceId}`,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hasMessageListFetchScheduled) {
|
||||
fieldOptions.push({
|
||||
value: MessageChannelSyncStage.MESSAGE_LIST_FETCH_SCHEDULED,
|
||||
label: 'Messages list fetch scheduled',
|
||||
position: 1,
|
||||
color: 'green',
|
||||
});
|
||||
}
|
||||
|
||||
if (!hasPendingConfiguration) {
|
||||
fieldOptions.push({
|
||||
value: MessageChannelSyncStage.PENDING_CONFIGURATION,
|
||||
label: 'Pending configuration',
|
||||
position: 9,
|
||||
color: 'gray',
|
||||
});
|
||||
}
|
||||
|
||||
syncStageField.options = fieldOptions;
|
||||
|
||||
await this.fieldMetadataRepository.save(syncStageField);
|
||||
|
||||
this.logger.log(
|
||||
`Updated MessageChannel syncStage field metadata for workspace ${workspaceId}`,
|
||||
);
|
||||
}
|
||||
|
||||
private async updateCalendarChannelSyncStageFieldMetadata(
|
||||
workspaceId: string,
|
||||
options: RunOnWorkspaceArgs['options'],
|
||||
): Promise<void> {
|
||||
const calendarChannelObject = await this.objectMetadataRepository.findOne({
|
||||
where: {
|
||||
standardId: STANDARD_OBJECT_IDS.calendarChannel,
|
||||
workspaceId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!calendarChannelObject) {
|
||||
this.logger.log(
|
||||
`CalendarChannel object not found for workspace ${workspaceId}, skipping field metadata update`,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const syncStageField = await this.fieldMetadataRepository.findOne({
|
||||
where: {
|
||||
standardId: CALENDAR_CHANNEL_STANDARD_FIELD_IDS.syncStage,
|
||||
objectMetadataId: calendarChannelObject.id,
|
||||
workspaceId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!syncStageField) {
|
||||
this.logger.log(
|
||||
`CalendarChannel syncStage field not found for workspace ${workspaceId}, skipping field metadata update`,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const fieldOptions = (syncStageField.options ||
|
||||
[]) as FieldMetadataComplexOption[];
|
||||
|
||||
const hasCalendarEventListFetchScheduled = fieldOptions.some(
|
||||
(option) =>
|
||||
option.value ===
|
||||
CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_SCHEDULED,
|
||||
);
|
||||
|
||||
const hasPendingConfiguration = fieldOptions.some(
|
||||
(option) =>
|
||||
option.value === CalendarChannelSyncStage.PENDING_CONFIGURATION,
|
||||
);
|
||||
|
||||
if (hasCalendarEventListFetchScheduled && hasPendingConfiguration) {
|
||||
this.logger.log(
|
||||
`CalendarChannel syncStage field metadata already migrated for workspace ${workspaceId}`,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (options.dryRun) {
|
||||
this.logger.log(
|
||||
`Would update CalendarChannel syncStage field metadata for workspace ${workspaceId}`,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hasCalendarEventListFetchScheduled) {
|
||||
fieldOptions.push({
|
||||
value: CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_SCHEDULED,
|
||||
label: 'Calendar event list fetch scheduled',
|
||||
position: 1,
|
||||
color: 'green',
|
||||
});
|
||||
}
|
||||
|
||||
if (!hasPendingConfiguration) {
|
||||
fieldOptions.push({
|
||||
value: CalendarChannelSyncStage.PENDING_CONFIGURATION,
|
||||
label: 'Pending configuration',
|
||||
position: 9,
|
||||
color: 'gray',
|
||||
});
|
||||
}
|
||||
|
||||
syncStageField.options = fieldOptions;
|
||||
|
||||
await this.fieldMetadataRepository.save(syncStageField);
|
||||
|
||||
this.logger.log(
|
||||
`Updated CalendarChannel syncStage field metadata for workspace ${workspaceId}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
+5
@@ -2,9 +2,11 @@ import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { DeduplicateUniqueFieldsCommand } from 'src/database/commands/upgrade-version-command/1-10/1-10-deduplicate-unique-fields.command';
|
||||
import { MigrateChannelSyncStagesCommand } from 'src/database/commands/upgrade-version-command/1-10/1-10-migrate-channel-sync-stages.command';
|
||||
import { MigrateWorkflowStepFilterOperandValueCommand } from 'src/database/commands/upgrade-version-command/1-10/1-10-migrate-workflow-step-filter-operand-value';
|
||||
import { RegeneratePersonSearchVectorWithPhonesCommand } from 'src/database/commands/upgrade-version-command/1-10/1-10-regenerate-person-search-vector-with-phones.command';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity';
|
||||
import { IndexMetadataModule } from 'src/engine/metadata-modules/index-metadata/index-metadata.module';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
@@ -16,6 +18,7 @@ import { WorkspaceMigrationRunnerModule } from 'src/engine/workspace-manager/wor
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([
|
||||
Workspace,
|
||||
FieldMetadataEntity,
|
||||
ObjectMetadataEntity,
|
||||
IndexMetadataEntity,
|
||||
]),
|
||||
@@ -26,10 +29,12 @@ import { WorkspaceMigrationRunnerModule } from 'src/engine/workspace-manager/wor
|
||||
],
|
||||
providers: [
|
||||
MigrateWorkflowStepFilterOperandValueCommand,
|
||||
MigrateChannelSyncStagesCommand,
|
||||
DeduplicateUniqueFieldsCommand,
|
||||
RegeneratePersonSearchVectorWithPhonesCommand,
|
||||
],
|
||||
exports: [
|
||||
MigrateChannelSyncStagesCommand,
|
||||
MigrateWorkflowStepFilterOperandValueCommand,
|
||||
DeduplicateUniqueFieldsCommand,
|
||||
RegeneratePersonSearchVectorWithPhonesCommand,
|
||||
|
||||
+22
-18
@@ -88,15 +88,16 @@ export class GoogleAPIsAuthController {
|
||||
|
||||
const handle = emails[0].value;
|
||||
|
||||
await this.googleAPIsService.refreshGoogleRefreshToken({
|
||||
handle,
|
||||
workspaceMemberId: workspaceMemberId,
|
||||
workspaceId: workspaceId,
|
||||
accessToken,
|
||||
refreshToken,
|
||||
calendarVisibility,
|
||||
messageVisibility,
|
||||
});
|
||||
const connectedAccountId =
|
||||
await this.googleAPIsService.refreshGoogleRefreshToken({
|
||||
handle,
|
||||
workspaceMemberId: workspaceMemberId,
|
||||
workspaceId: workspaceId,
|
||||
accessToken,
|
||||
refreshToken,
|
||||
calendarVisibility,
|
||||
messageVisibility,
|
||||
});
|
||||
|
||||
if (userId) {
|
||||
await this.onboardingService.setOnboardingConnectAccountPending({
|
||||
@@ -113,15 +114,18 @@ export class GoogleAPIsAuthController {
|
||||
);
|
||||
}
|
||||
|
||||
return res.redirect(
|
||||
this.domainManagerService
|
||||
.buildWorkspaceURL({
|
||||
workspace,
|
||||
pathname:
|
||||
redirectLocation || getSettingsPath(SettingsPath.Accounts),
|
||||
})
|
||||
.toString(),
|
||||
);
|
||||
const pathname =
|
||||
redirectLocation ||
|
||||
getSettingsPath(SettingsPath.AccountsConfiguration, {
|
||||
connectedAccountId,
|
||||
});
|
||||
|
||||
const url = this.domainManagerService.buildWorkspaceURL({
|
||||
workspace,
|
||||
pathname,
|
||||
});
|
||||
|
||||
return res.redirect(url.toString());
|
||||
} catch (error) {
|
||||
return res.redirect(
|
||||
this.guardRedirectService.getRedirectErrorUrlAndCaptureExceptions({
|
||||
|
||||
+22
-18
@@ -95,15 +95,16 @@ export class MicrosoftAPIsAuthController {
|
||||
|
||||
const handle = emails[0].value;
|
||||
|
||||
await this.microsoftAPIsService.refreshMicrosoftRefreshToken({
|
||||
handle,
|
||||
workspaceMemberId: workspaceMemberId,
|
||||
workspaceId: workspaceId,
|
||||
accessToken,
|
||||
refreshToken,
|
||||
calendarVisibility,
|
||||
messageVisibility,
|
||||
});
|
||||
const connectedAccountId =
|
||||
await this.microsoftAPIsService.refreshMicrosoftRefreshToken({
|
||||
handle,
|
||||
workspaceMemberId: workspaceMemberId,
|
||||
workspaceId: workspaceId,
|
||||
accessToken,
|
||||
refreshToken,
|
||||
calendarVisibility,
|
||||
messageVisibility,
|
||||
});
|
||||
|
||||
if (userId) {
|
||||
await this.onboardingService.setOnboardingConnectAccountPending({
|
||||
@@ -120,15 +121,18 @@ export class MicrosoftAPIsAuthController {
|
||||
);
|
||||
}
|
||||
|
||||
return res.redirect(
|
||||
this.domainManagerService
|
||||
.buildWorkspaceURL({
|
||||
workspace,
|
||||
pathname:
|
||||
redirectLocation || getSettingsPath(SettingsPath.Accounts),
|
||||
})
|
||||
.toString(),
|
||||
);
|
||||
const pathname =
|
||||
redirectLocation ||
|
||||
getSettingsPath(SettingsPath.AccountsConfiguration, {
|
||||
connectedAccountId,
|
||||
});
|
||||
|
||||
const url = this.domainManagerService.buildWorkspaceURL({
|
||||
workspace,
|
||||
pathname,
|
||||
});
|
||||
|
||||
return res.redirect(url.toString());
|
||||
} catch (error) {
|
||||
return res.redirect(
|
||||
this.guardRedirectService.getRedirectErrorUrlAndCaptureExceptions({
|
||||
|
||||
+4
@@ -5,6 +5,8 @@ import { v4 } from 'uuid';
|
||||
import { type WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import {
|
||||
CalendarChannelSyncStage,
|
||||
CalendarChannelSyncStatus,
|
||||
CalendarChannelVisibility,
|
||||
type CalendarChannelWorkspaceEntity,
|
||||
} from 'src/modules/calendar/common/standard-objects/calendar-channel.workspace-entity';
|
||||
@@ -47,6 +49,8 @@ export class CreateCalendarChannelService {
|
||||
handle,
|
||||
visibility:
|
||||
calendarVisibility || CalendarChannelVisibility.SHARE_EVERYTHING,
|
||||
syncStatus: CalendarChannelSyncStatus.NOT_SYNCED,
|
||||
syncStage: CalendarChannelSyncStage.PENDING_CONFIGURATION,
|
||||
},
|
||||
{},
|
||||
manager,
|
||||
|
||||
+3
-1
@@ -5,6 +5,7 @@ import { v4 } from 'uuid';
|
||||
import { type WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import {
|
||||
MessageChannelSyncStage,
|
||||
MessageChannelSyncStatus,
|
||||
MessageChannelType,
|
||||
MessageChannelVisibility,
|
||||
@@ -50,7 +51,8 @@ export class CreateMessageChannelService {
|
||||
handle,
|
||||
visibility:
|
||||
messageVisibility || MessageChannelVisibility.SHARE_EVERYTHING,
|
||||
syncStatus: MessageChannelSyncStatus.ONGOING,
|
||||
syncStatus: MessageChannelSyncStatus.NOT_SYNCED,
|
||||
syncStage: MessageChannelSyncStage.PENDING_CONFIGURATION,
|
||||
},
|
||||
{},
|
||||
manager,
|
||||
|
||||
+29
-15
@@ -25,12 +25,14 @@ import {
|
||||
type CalendarEventListFetchJobData,
|
||||
} from 'src/modules/calendar/calendar-event-import-manager/jobs/calendar-event-list-fetch.job';
|
||||
import {
|
||||
CalendarChannelSyncStage,
|
||||
type CalendarChannelVisibility,
|
||||
type CalendarChannelWorkspaceEntity,
|
||||
} from 'src/modules/calendar/common/standard-objects/calendar-channel.workspace-entity';
|
||||
import { AccountsToReconnectService } from 'src/modules/connected-account/services/accounts-to-reconnect.service';
|
||||
import { type ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
|
||||
import {
|
||||
MessageChannelSyncStage,
|
||||
type MessageChannelVisibility,
|
||||
type MessageChannelWorkspaceEntity,
|
||||
} from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity';
|
||||
@@ -67,7 +69,7 @@ export class GoogleAPIsService {
|
||||
refreshToken: string;
|
||||
calendarVisibility: CalendarChannelVisibility | undefined;
|
||||
messageVisibility: MessageChannelVisibility | undefined;
|
||||
}) {
|
||||
}): Promise<string> {
|
||||
const {
|
||||
handle,
|
||||
workspaceId,
|
||||
@@ -210,13 +212,18 @@ export class GoogleAPIsService {
|
||||
});
|
||||
|
||||
for (const messageChannel of messageChannels) {
|
||||
await this.messageQueueService.add<MessagingMessageListFetchJobData>(
|
||||
MessagingMessageListFetchJob.name,
|
||||
{
|
||||
workspaceId,
|
||||
messageChannelId: messageChannel.id,
|
||||
},
|
||||
);
|
||||
if (
|
||||
messageChannel.syncStage !==
|
||||
MessageChannelSyncStage.PENDING_CONFIGURATION
|
||||
) {
|
||||
await this.messageQueueService.add<MessagingMessageListFetchJobData>(
|
||||
MessagingMessageListFetchJob.name,
|
||||
{
|
||||
workspaceId,
|
||||
messageChannelId: messageChannel.id,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,14 +235,21 @@ export class GoogleAPIsService {
|
||||
});
|
||||
|
||||
for (const calendarChannel of calendarChannels) {
|
||||
await this.calendarQueueService.add<CalendarEventListFetchJobData>(
|
||||
CalendarEventListFetchJob.name,
|
||||
{
|
||||
calendarChannelId: calendarChannel.id,
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
if (
|
||||
calendarChannel.syncStage !==
|
||||
CalendarChannelSyncStage.PENDING_CONFIGURATION
|
||||
) {
|
||||
await this.calendarQueueService.add<CalendarEventListFetchJobData>(
|
||||
CalendarEventListFetchJob.name,
|
||||
{
|
||||
calendarChannelId: calendarChannel.id,
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return newOrExistingConnectedAccountId;
|
||||
}
|
||||
}
|
||||
|
||||
+29
-15
@@ -22,12 +22,14 @@ import {
|
||||
type CalendarEventListFetchJobData,
|
||||
} from 'src/modules/calendar/calendar-event-import-manager/jobs/calendar-event-list-fetch.job';
|
||||
import {
|
||||
CalendarChannelSyncStage,
|
||||
type CalendarChannelVisibility,
|
||||
type CalendarChannelWorkspaceEntity,
|
||||
} from 'src/modules/calendar/common/standard-objects/calendar-channel.workspace-entity';
|
||||
import { AccountsToReconnectService } from 'src/modules/connected-account/services/accounts-to-reconnect.service';
|
||||
import { type ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
|
||||
import {
|
||||
MessageChannelSyncStage,
|
||||
type MessageChannelVisibility,
|
||||
type MessageChannelWorkspaceEntity,
|
||||
} from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity';
|
||||
@@ -64,7 +66,7 @@ export class MicrosoftAPIsService {
|
||||
refreshToken: string;
|
||||
calendarVisibility: CalendarChannelVisibility | undefined;
|
||||
messageVisibility: MessageChannelVisibility | undefined;
|
||||
}) {
|
||||
}): Promise<string> {
|
||||
const {
|
||||
handle,
|
||||
workspaceId,
|
||||
@@ -201,13 +203,18 @@ export class MicrosoftAPIsService {
|
||||
});
|
||||
|
||||
for (const messageChannel of messageChannels) {
|
||||
await this.messageQueueService.add<MessagingMessageListFetchJobData>(
|
||||
MessagingMessageListFetchJob.name,
|
||||
{
|
||||
workspaceId,
|
||||
messageChannelId: messageChannel.id,
|
||||
},
|
||||
);
|
||||
if (
|
||||
messageChannel.syncStage !==
|
||||
MessageChannelSyncStage.PENDING_CONFIGURATION
|
||||
) {
|
||||
await this.messageQueueService.add<MessagingMessageListFetchJobData>(
|
||||
MessagingMessageListFetchJob.name,
|
||||
{
|
||||
workspaceId,
|
||||
messageChannelId: messageChannel.id,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,14 +226,21 @@ export class MicrosoftAPIsService {
|
||||
});
|
||||
|
||||
for (const calendarChannel of calendarChannels) {
|
||||
await this.calendarQueueService.add<CalendarEventListFetchJobData>(
|
||||
CalendarEventListFetchJob.name,
|
||||
{
|
||||
calendarChannelId: calendarChannel.id,
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
if (
|
||||
calendarChannel.syncStage !==
|
||||
CalendarChannelSyncStage.PENDING_CONFIGURATION
|
||||
) {
|
||||
await this.calendarQueueService.add<CalendarEventListFetchJobData>(
|
||||
CalendarEventListFetchJob.name,
|
||||
{
|
||||
calendarChannelId: calendarChannel.id,
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return newOrExistingConnectedAccountId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +57,7 @@ import { WorkspaceModule } from 'src/engine/core-modules/workspace/workspace.mod
|
||||
import { RoleModule } from 'src/engine/metadata-modules/role/role.module';
|
||||
import { SubscriptionsModule } from 'src/engine/subscriptions/subscriptions.module';
|
||||
import { WorkspaceEventEmitterModule } from 'src/engine/workspace-event-emitter/workspace-event-emitter.module';
|
||||
import { ChannelSyncModule } from 'src/modules/connected-account/channel-sync/channel-sync.module';
|
||||
|
||||
import { AuditModule } from './audit/audit.module';
|
||||
import { ClientConfigModule } from './client-config/client-config.module';
|
||||
@@ -100,6 +101,7 @@ import { FileModule } from './file/file.module';
|
||||
GeoMapModule,
|
||||
SubscriptionsModule,
|
||||
ImapSmtpCaldavModule,
|
||||
ChannelSyncModule,
|
||||
FileStorageModule.forRoot(),
|
||||
LoggerModule.forRootAsync({
|
||||
useFactory: loggerModuleFactory,
|
||||
|
||||
+3
@@ -4,4 +4,7 @@ import { Field, ObjectType } from '@nestjs/graphql';
|
||||
export class ImapSmtpCaldavConnectionSuccess {
|
||||
@Field(() => Boolean)
|
||||
success: boolean;
|
||||
|
||||
@Field(() => String)
|
||||
connectedAccountId: string;
|
||||
}
|
||||
|
||||
+9
-7
@@ -103,16 +103,18 @@ export class ImapSmtpCaldavResolver {
|
||||
handle,
|
||||
);
|
||||
|
||||
await this.imapSmtpCaldavApisService.processAccount({
|
||||
handle,
|
||||
workspaceMemberId: accountOwnerId,
|
||||
workspaceId: workspace.id,
|
||||
connectionParameters: validatedParams,
|
||||
connectedAccountId: id,
|
||||
});
|
||||
const connectedAccountId =
|
||||
await this.imapSmtpCaldavApisService.processAccount({
|
||||
handle,
|
||||
workspaceMemberId: accountOwnerId,
|
||||
workspaceId: workspace.id,
|
||||
connectionParameters: validatedParams,
|
||||
connectedAccountId: id,
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
connectedAccountId,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+8
-1
@@ -35,6 +35,7 @@ export enum CalendarChannelSyncStatus {
|
||||
}
|
||||
|
||||
export enum CalendarChannelSyncStage {
|
||||
PENDING_CONFIGURATION = 'PENDING_CONFIGURATION',
|
||||
FULL_CALENDAR_EVENT_LIST_FETCH_PENDING = 'FULL_CALENDAR_EVENT_LIST_FETCH_PENDING', // WILL BE DEPRECATED
|
||||
PARTIAL_CALENDAR_EVENT_LIST_FETCH_PENDING = 'PARTIAL_CALENDAR_EVENT_LIST_FETCH_PENDING', // DEPRECATED
|
||||
CALENDAR_EVENT_LIST_FETCH_PENDING = 'CALENDAR_EVENT_LIST_FETCH_PENDING',
|
||||
@@ -194,8 +195,14 @@ export class CalendarChannelWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
position: 8,
|
||||
color: 'blue',
|
||||
},
|
||||
{
|
||||
value: CalendarChannelSyncStage.PENDING_CONFIGURATION,
|
||||
label: 'Pending configuration',
|
||||
position: 9,
|
||||
color: 'gray',
|
||||
},
|
||||
],
|
||||
defaultValue: `'${CalendarChannelSyncStage.FULL_CALENDAR_EVENT_LIST_FETCH_PENDING}'`,
|
||||
defaultValue: `'${CalendarChannelSyncStage.PENDING_CONFIGURATION}'`,
|
||||
})
|
||||
syncStage: CalendarChannelSyncStage;
|
||||
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
|
||||
import { ChannelSyncResolver } from 'src/modules/connected-account/channel-sync/channel-sync.resolver';
|
||||
import { ChannelSyncService } from 'src/modules/connected-account/channel-sync/services/channel-sync.service';
|
||||
|
||||
@Module({
|
||||
imports: [WorkspaceDataSourceModule],
|
||||
providers: [ChannelSyncResolver, ChannelSyncService],
|
||||
exports: [ChannelSyncService],
|
||||
})
|
||||
export class ChannelSyncModule {}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
import { UseFilters, UseGuards, UsePipes } from '@nestjs/common';
|
||||
import { Args, Mutation, Resolver } from '@nestjs/graphql';
|
||||
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
import { AuthGraphqlApiExceptionFilter } from 'src/engine/core-modules/auth/filters/auth-graphql-api-exception.filter';
|
||||
import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
|
||||
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
|
||||
import { ChannelSyncSuccess } from 'src/modules/connected-account/channel-sync/dtos/channel-sync-success.dto';
|
||||
import { ChannelSyncService } from 'src/modules/connected-account/channel-sync/services/channel-sync.service';
|
||||
|
||||
@Resolver()
|
||||
@UsePipes(ResolverValidationPipe)
|
||||
@UseFilters(AuthGraphqlApiExceptionFilter)
|
||||
@UseGuards(WorkspaceAuthGuard)
|
||||
export class ChannelSyncResolver {
|
||||
constructor(private readonly channelSyncService: ChannelSyncService) {}
|
||||
|
||||
@Mutation(() => ChannelSyncSuccess)
|
||||
async startChannelSync(
|
||||
@Args('connectedAccountId', { type: () => UUIDScalarType })
|
||||
connectedAccountId: string,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
): Promise<ChannelSyncSuccess> {
|
||||
await this.channelSyncService.startChannelSync({
|
||||
connectedAccountId,
|
||||
workspaceId: workspace.id,
|
||||
});
|
||||
|
||||
return { success: true };
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
import { Field, ObjectType } from '@nestjs/graphql';
|
||||
|
||||
@ObjectType()
|
||||
export class ChannelSyncSuccess {
|
||||
@Field(() => Boolean)
|
||||
success: boolean;
|
||||
}
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { InjectMessageQueue } from 'src/engine/core-modules/message-queue/decorators/message-queue.decorator';
|
||||
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
|
||||
import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import {
|
||||
CalendarEventListFetchJob,
|
||||
type CalendarEventListFetchJobData,
|
||||
} from 'src/modules/calendar/calendar-event-import-manager/jobs/calendar-event-list-fetch.job';
|
||||
import {
|
||||
CalendarChannelSyncStage,
|
||||
CalendarChannelSyncStatus,
|
||||
type CalendarChannelWorkspaceEntity,
|
||||
} from 'src/modules/calendar/common/standard-objects/calendar-channel.workspace-entity';
|
||||
import {
|
||||
MessageChannelSyncStage,
|
||||
MessageChannelSyncStatus,
|
||||
type MessageChannelWorkspaceEntity,
|
||||
} from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity';
|
||||
import {
|
||||
MessagingMessageListFetchJob,
|
||||
type MessagingMessageListFetchJobData,
|
||||
} from 'src/modules/messaging/message-import-manager/jobs/messaging-message-list-fetch.job';
|
||||
|
||||
export type StartChannelSyncInput = {
|
||||
connectedAccountId: string;
|
||||
workspaceId: string;
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class ChannelSyncService {
|
||||
constructor(
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
@InjectMessageQueue(MessageQueue.messagingQueue)
|
||||
private readonly messageQueueService: MessageQueueService,
|
||||
@InjectMessageQueue(MessageQueue.calendarQueue)
|
||||
private readonly calendarQueueService: MessageQueueService,
|
||||
) {}
|
||||
|
||||
async startChannelSync(input: StartChannelSyncInput): Promise<void> {
|
||||
const { connectedAccountId, workspaceId } = input;
|
||||
|
||||
await this.startMessageChannelSync(connectedAccountId, workspaceId);
|
||||
await this.startCalendarChannelSync(connectedAccountId, workspaceId);
|
||||
}
|
||||
|
||||
private async startMessageChannelSync(
|
||||
connectedAccountId: string,
|
||||
workspaceId: string,
|
||||
): Promise<void> {
|
||||
const messageChannelRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<MessageChannelWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'messageChannel',
|
||||
);
|
||||
|
||||
const messageChannels = await messageChannelRepository.find({
|
||||
where: {
|
||||
connectedAccountId,
|
||||
syncStage: MessageChannelSyncStage.PENDING_CONFIGURATION,
|
||||
},
|
||||
});
|
||||
|
||||
for (const messageChannel of messageChannels) {
|
||||
await this.messageQueueService.add<MessagingMessageListFetchJobData>(
|
||||
MessagingMessageListFetchJob.name,
|
||||
{
|
||||
workspaceId,
|
||||
messageChannelId: messageChannel.id,
|
||||
},
|
||||
);
|
||||
|
||||
await messageChannelRepository.update(messageChannel.id, {
|
||||
syncStage: MessageChannelSyncStage.MESSAGE_LIST_FETCH_PENDING,
|
||||
syncStatus: MessageChannelSyncStatus.ONGOING,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async startCalendarChannelSync(
|
||||
connectedAccountId: string,
|
||||
workspaceId: string,
|
||||
): Promise<void> {
|
||||
const calendarChannelRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<CalendarChannelWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'calendarChannel',
|
||||
);
|
||||
|
||||
const calendarChannels = await calendarChannelRepository.find({
|
||||
where: {
|
||||
connectedAccountId,
|
||||
syncStage: CalendarChannelSyncStage.PENDING_CONFIGURATION,
|
||||
},
|
||||
});
|
||||
|
||||
for (const calendarChannel of calendarChannels) {
|
||||
await this.calendarQueueService.add<CalendarEventListFetchJobData>(
|
||||
CalendarEventListFetchJob.name,
|
||||
{
|
||||
calendarChannelId: calendarChannel.id,
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
await calendarChannelRepository.update(calendarChannel.id, {
|
||||
syncStage: CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_SCHEDULED,
|
||||
syncStatus: CalendarChannelSyncStatus.ONGOING,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -46,7 +46,7 @@ export class ImapSmtpCalDavAPIService {
|
||||
workspaceId: string;
|
||||
connectionParameters: EmailAccountConnectionParameters;
|
||||
connectedAccountId?: string;
|
||||
}) {
|
||||
}): Promise<string> {
|
||||
const { handle, workspaceId, workspaceMemberId, connectedAccountId } =
|
||||
input;
|
||||
|
||||
@@ -125,6 +125,8 @@ export class ImapSmtpCalDavAPIService {
|
||||
messageChannel,
|
||||
calendarChannel,
|
||||
);
|
||||
|
||||
return accountId;
|
||||
}
|
||||
|
||||
private async upsertConnectedAccount(
|
||||
|
||||
+8
-1
@@ -31,6 +31,7 @@ export enum MessageChannelSyncStatus {
|
||||
}
|
||||
|
||||
export enum MessageChannelSyncStage {
|
||||
PENDING_CONFIGURATION = 'PENDING_CONFIGURATION',
|
||||
FULL_MESSAGE_LIST_FETCH_PENDING = 'FULL_MESSAGE_LIST_FETCH_PENDING', // WILL BE DEPRECATED
|
||||
PARTIAL_MESSAGE_LIST_FETCH_PENDING = 'PARTIAL_MESSAGE_LIST_FETCH_PENDING', // DEPRECATED
|
||||
MESSAGE_LIST_FETCH_PENDING = 'MESSAGE_LIST_FETCH_PENDING',
|
||||
@@ -380,8 +381,14 @@ export class MessageChannelWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
position: 8,
|
||||
color: 'blue',
|
||||
},
|
||||
{
|
||||
value: MessageChannelSyncStage.PENDING_CONFIGURATION,
|
||||
label: 'Pending configuration',
|
||||
position: 9,
|
||||
color: 'gray',
|
||||
},
|
||||
],
|
||||
defaultValue: `'${MessageChannelSyncStage.FULL_MESSAGE_LIST_FETCH_PENDING}'`,
|
||||
defaultValue: `'${MessageChannelSyncStage.PENDING_CONFIGURATION}'`,
|
||||
})
|
||||
syncStage: MessageChannelSyncStage;
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ export enum SettingsPath {
|
||||
Experience = 'experience',
|
||||
Accounts = 'accounts',
|
||||
NewAccount = 'accounts/new',
|
||||
AccountsConfiguration = 'accounts/configuration/:connectedAccountId',
|
||||
AccountsCalendars = 'accounts/calendars',
|
||||
AccountsEmails = 'accounts/emails',
|
||||
NewImapSmtpCaldavConnection = 'accounts/new-imap-smtp-caldav-connection',
|
||||
|
||||
Reference in New Issue
Block a user