feat: CalDav Driver (#13170)

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
This commit is contained in:
neo773
2025-07-15 21:11:23 +05:30
committed by GitHub
parent c5a74b8e92
commit 3e8fa3120d
22 changed files with 1210 additions and 339 deletions

View File

@ -291,6 +291,22 @@ export const SettingsAccountsConnectionForm = ({
)}
/>
<Controller
name="CALDAV.username"
control={control}
render={({ field, fieldState }) => (
<TextInput
instanceId="caldav-username-connection-form"
label={t`CalDAV Username`}
placeholder={t`john.doe`}
required={false}
value={field.value || ''}
onChange={field.onChange}
error={fieldState.error?.message}
/>
)}
/>
<Controller
name="CALDAV.password"
control={control}
@ -306,47 +322,6 @@ export const SettingsAccountsConnectionForm = ({
/>
)}
/>
<StyledFieldRow>
<StyledFieldGroup>
<Controller
name="CALDAV.port"
control={control}
render={({ field, fieldState }) => (
<TextInput
instanceId="caldav-port-connection-form"
label={t`CalDAV Port`}
type="number"
placeholder="443"
value={field?.value ? field.value : 443}
onChange={(value) =>
field.onChange(handlePortChange(value))
}
error={fieldState.error?.message}
/>
)}
/>
</StyledFieldGroup>
<StyledFieldGroup>
<Controller
name="CALDAV.secure"
control={control}
render={({ field }) => (
<Select
label={t`CalDAV Encryption`}
options={[
{ label: 'SSL/TLS', value: true },
{ label: 'None', value: false },
]}
value={field.value}
onChange={field.onChange}
dropdownId="caldav-secure-dropdown"
/>
)}
/>
</StyledFieldGroup>
</StyledFieldRow>
</StyledConnectionSection>
</StyledFormContainer>
</Section>

View File

@ -1,17 +1,15 @@
import gql from 'graphql-tag';
export const SAVE_IMAP_SMTP_CALDAV_CONNECTION = gql`
mutation SaveImapSmtpCaldav(
export const SAVE_IMAP_SMTP_CALDAV_ACCOUNT = gql`
mutation SaveImapSmtpCaldavAccount(
$accountOwnerId: String!
$handle: String!
$accountType: AccountType!
$connectionParameters: ConnectionParameters!
$connectionParameters: EmailAccountConnectionParameters!
$id: String
) {
saveImapSmtpCaldav(
saveImapSmtpCaldavAccount(
accountOwnerId: $accountOwnerId
handle: $handle
accountType: $accountType
connectionParameters: $connectionParameters
id: $id
) {

View File

@ -22,8 +22,7 @@ export const GET_CONNECTED_IMAP_SMTP_CALDAV_ACCOUNT = gql`
}
CALDAV {
host
port
secure
username
password
}
}

View File

@ -10,7 +10,7 @@ import { SettingsPath } from '@/types/SettingsPath';
import { t } from '@lingui/core/macro';
import {
ConnectionParameters,
useSaveImapSmtpCaldavMutation,
useSaveImapSmtpCaldavAccountMutation,
} from '~/generated-metadata/graphql';
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
@ -49,7 +49,13 @@ export const useImapSmtpCaldavConnectionForm = ({
handle: '',
IMAP: { host: '', port: 993, password: '', secure: true },
SMTP: { host: '', port: 587, password: '', secure: true },
CALDAV: { host: '', port: 443, password: '', secure: true },
CALDAV: {
host: '',
port: 443,
password: '',
secure: true,
username: undefined,
},
},
});
@ -76,7 +82,7 @@ export const useImapSmtpCaldavConnectionForm = ({
);
const [saveConnection, { loading: saveLoading }] =
useSaveImapSmtpCaldavMutation();
useSaveImapSmtpCaldavAccountMutation();
const watchedValues = watch();
@ -102,47 +108,39 @@ export const useImapSmtpCaldavConnectionForm = ({
);
}, [getConfiguredProtocols, watchedValues.handle]);
const saveIndividualConnection = useCallback(
async (
protocol: keyof ImapSmtpCaldavAccount,
formValues: ConnectionFormData,
): Promise<void> => {
const handleSave = useCallback(
async (formValues: ConnectionFormData): Promise<void> => {
if (!currentWorkspaceMember?.id) {
throw new Error('Workspace member ID is missing');
}
const protocolConfig = formValues[protocol];
if (!protocolConfig) {
throw new Error(`${protocol} configuration is missing`);
}
await saveConnection({
variables: {
...(isEditing && connectedAccountId
? { id: connectedAccountId }
: {}),
accountOwnerId: currentWorkspaceMember.id,
handle: formValues.handle,
accountType: {
type: protocol,
},
connectionParameters: protocolConfig,
},
});
},
[saveConnection, isEditing, connectedAccountId, currentWorkspaceMember?.id],
);
const handleSave = useCallback(
async (formValues: ConnectionFormData): Promise<void> => {
const configuredProtocols = getConfiguredProtocols(formValues);
if (configuredProtocols.length === 0) {
throw new Error('At least one protocol must be configured');
}
const connectionParameters: Partial<
Record<keyof ImapSmtpCaldavAccount, ConnectionParameters>
> = {};
configuredProtocols.forEach((protocol) => {
const protocolConfig = formValues[protocol];
if (isDefined(protocolConfig)) {
connectionParameters[protocol] = protocolConfig;
}
});
try {
await Promise.all(
configuredProtocols.map((protocol) =>
saveIndividualConnection(protocol, formValues),
),
);
await saveConnection({
variables: {
...(isEditing && connectedAccountId
? { id: connectedAccountId }
: {}),
accountOwnerId: currentWorkspaceMember.id,
handle: formValues.handle,
connectionParameters,
},
});
const successMessage = isEditing
? t`Connection successfully updated`
@ -160,12 +158,14 @@ export const useImapSmtpCaldavConnectionForm = ({
}
},
[
currentWorkspaceMember?.id,
getConfiguredProtocols,
saveIndividualConnection,
saveConnection,
isEditing,
connectedAccountId,
enqueueSuccessSnackBar,
enqueueErrorSnackBar,
navigate,
enqueueErrorSnackBar,
],
);

View File

@ -6,6 +6,7 @@ const connectionParameters = z
.object({
host: z.string().default(''),
port: z.number().int().nullable().default(null),
username: z.string().optional(),
password: z.string().default(''),
secure: z.boolean().default(true),
})