Replace the update provider wizard with a settings card

This commit is contained in:
Steven Nguyen
2024-02-21 10:28:22 +01:00
committed by Torsten Dittmann
parent e6d80b0966
commit dcaa8eb2f5
6 changed files with 428 additions and 354 deletions
@@ -11,6 +11,50 @@
FCM = 'fcm',
APNS = 'apns'
}
export function getProviderDisplayNameAndIcon(provider: Providers | string) {
let icon = '';
let displayName = provider.charAt(0).toUpperCase() + provider.slice(1);
switch (provider) {
case Providers.FCM:
icon = 'firebase';
displayName = 'FCM';
break;
case Providers.APNS:
icon = 'apple';
displayName = 'APNS';
break;
case Providers.Sendgrid:
icon = 'sendgrid';
break;
case Providers.Mailgun:
icon = 'mailgun';
break;
case Providers.SMTP:
icon = 'smtp';
displayName = 'SMTP';
break;
case Providers.Twilio:
icon = 'twilio';
break;
case Providers.Telesign:
icon = 'telesign';
break;
case Providers.Msg91:
icon = 'msg91';
displayName = 'MSG91';
break;
case Providers.Textmagic:
icon = 'textmagic';
displayName = 'Textmagic';
break;
case Providers.Vonage:
icon = 'vonage';
break;
}
return { icon, displayName };
}
</script>
<script lang="ts">
@@ -22,8 +66,7 @@
export let noIcon = false;
export let size: 's' | 'm' | 'l' = 'm';
let icon = '';
let displayName = name || provider.charAt(0).toUpperCase() + provider.slice(1);
const { icon, displayName } = getProviderDisplayNameAndIcon(provider);
let textSize = '1.25rem';
switch (size) {
@@ -34,44 +77,6 @@
textSize = '1.5rem';
break;
}
switch (provider) {
case Providers.FCM:
icon = 'firebase';
displayName = name || 'FCM';
break;
case Providers.APNS:
icon = 'apple';
displayName = name || 'APNS';
break;
case Providers.Sendgrid:
icon = 'sendgrid';
break;
case Providers.Mailgun:
icon = 'mailgun';
break;
case Providers.SMTP:
icon = 'smtp';
displayName = name || 'SMTP';
break;
case Providers.Twilio:
icon = 'twilio';
break;
case Providers.Telesign:
icon = 'telesign';
break;
case Providers.Msg91:
icon = 'msg91';
displayName = name || 'MSG91';
break;
case Providers.Textmagic:
icon = 'textmagic';
displayName = name || 'TextMagic';
break;
case Providers.Vonage:
icon = 'vonage';
break;
}
</script>
{#if icon === ''}
@@ -89,12 +94,12 @@
<img
style:--p-text-size={textSize}
src={`${base}/icons/${$app.themeInUse}/color/${icon}.svg`}
alt={displayName} />
alt={name || displayName} />
{/if}
</div>
{/if}
<slot>
{displayName}
{name || displayName}
</slot>
</div>
{/if}
@@ -2,11 +2,158 @@
import { Container } from '$lib/layout';
import DangerZone from './dangerZone.svelte';
import UpdateName from './updateName.svelte';
import UpdateSettings from './updateSettings.svelte';
import UpdateStatus from './updateStatus.svelte';
import { isValueOfStringEnum } from '$lib/helpers/types';
import { MessagingProviderType } from '@appwrite.io/console';
import { Providers } from '../../provider.svelte';
import { providers } from '../store';
import { provider as providerData } from './store';
let providerType: MessagingProviderType;
if (isValueOfStringEnum(MessagingProviderType, $providerData.type)) {
providerType = $providerData.type;
}
let provider: Providers;
if (isValueOfStringEnum(Providers, $providerData.provider)) {
provider = $providerData.provider;
}
let params: object = {};
$: switch (provider) {
case Providers.Twilio:
params = {
providerId: $providerData.$id,
name: $providerData.name,
enabled: $providerData.enabled,
accountSid: $providerData.credentials['accountSid'],
authToken: $providerData.credentials['authToken'],
from: $providerData.options['from']
};
break;
case Providers.Msg91:
params = {
providerId: $providerData.$id,
name: $providerData.name,
enabled: $providerData.enabled,
from: $providerData.options['from'],
senderId: $providerData.credentials['senderId'],
authKey: $providerData.credentials['authKey']
};
break;
case Providers.Telesign:
params = {
providerId: $providerData.$id,
name: $providerData.name,
enabled: $providerData.enabled,
username: $providerData.credentials['username'],
password: $providerData.credentials['password'],
from: $providerData.options['from']
};
break;
case Providers.Textmagic:
params = {
providerId: $providerData.$id,
name: $providerData.name,
enabled: $providerData.enabled,
username: $providerData.credentials['username'],
apiKey: $providerData.credentials['apiKey'],
from: $providerData.options['from']
};
break;
case Providers.Vonage:
params = {
providerId: $providerData.$id,
name: $providerData.name,
enabled: $providerData.enabled,
apiKey: $providerData.credentials['apiKey'],
apiSecret: $providerData.credentials['apiSecret'],
from: $providerData.options['from']
};
break;
case Providers.Mailgun:
params = {
providerId: $providerData.$id,
name: $providerData.name,
enabled: $providerData.enabled,
isEuRegion: false,
fromEmail: $providerData.options['fromEmail'],
fromName: $providerData.options['fromName'],
replyToEmail: $providerData.options['replyToEmail'],
replyToName: $providerData.options['replyToName'],
apiKey: $providerData.credentials['apiKey'],
domain: $providerData.credentials['domain']
};
break;
case Providers.Sendgrid:
params = {
providerId: $providerData.$id,
name: $providerData.name,
enabled: $providerData.enabled,
apiKey: $providerData.credentials['apiKey'],
fromEmail: $providerData.options['fromEmail'],
fromName: $providerData.options['fromName'],
replyToEmail: $providerData.options['replyToEmail'],
replyToName: $providerData.options['replyToName']
};
break;
case Providers.SMTP:
params = {
providerId: $providerData.$id,
name: $providerData.name,
enabled: $providerData.enabled,
host: $providerData.credentials['host'],
port: $providerData.credentials['port'],
username: $providerData.credentials['username'],
password: $providerData.credentials['password'],
fromName: $providerData.options['fromName'],
fromEmail: $providerData.options['fromEmail'],
replyToName: $providerData.options['replyToName'],
replyToEmail: $providerData.options['replyToEmail'],
encryption: $providerData.options['encryption'],
autoTLS: $providerData.options['autoTLS'],
mailer: $providerData.options['mailer']
};
break;
case Providers.FCM:
params = {
providerId: $providerData.$id,
name: $providerData.name,
enabled: $providerData.enabled
};
if ('serviceAccountJSON' in $providerData.credentials) {
const serviceAccountJSON = $providerData.credentials['serviceAccountJSON'];
if (typeof serviceAccountJSON === 'string') {
params['serviceAccountJSON'] = serviceAccountJSON;
} else if (serviceAccountJSON instanceof Object) {
params['serviceAccountJSON'] = JSON.stringify(serviceAccountJSON);
}
}
break;
case Providers.APNS:
params = {
providerId: $providerData.$id,
name: $providerData.name,
enabled: $providerData.enabled,
authKey: $providerData.credentials['authKey'],
authKeyId: $providerData.credentials['authKeyId'],
teamId: $providerData.credentials['teamId'],
bundleId: $providerData.credentials['bundleId']
};
break;
}
</script>
<Container>
<UpdateStatus />
<UpdateName />
<UpdateSettings
inputs={providers[providerType].providers[provider].configure}
providerId={$providerData.$id}
{providerType}
{provider}
{params} />
<DangerZone />
</Container>
@@ -2,8 +2,4 @@ import { derived } from 'svelte/store';
import { page } from '$app/stores';
import type { Models } from '@appwrite.io/console';
export const provider = derived(
page,
// TODO: Set actual type
($page) => $page.data.provider as Models.Provider
);
export const provider = derived(page, ($page) => $page.data.provider as Models.Provider);
@@ -0,0 +1,233 @@
<script lang="ts">
import { sdk } from '$lib/stores/sdk';
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { addNotification } from '$lib/stores/notifications';
import { invalidate } from '$app/navigation';
import type { ProviderInput } from '../store';
import { Providers, getProviderDisplayNameAndIcon } from '../../provider.svelte';
import { Dependencies } from '$lib/constants';
import { MessagingProviderType, type Models } from '@appwrite.io/console';
import SettingsFormList from '../settingsFormList.svelte';
import { CardGrid, Heading } from '$lib/components';
import { Button, Form } from '$lib/elements/forms';
import { onMount } from 'svelte';
import CreateMember from '$routes/console/organization-[organization]/createMember.svelte';
export let inputs: ProviderInput[];
export let providerType: MessagingProviderType;
export let provider: Providers;
export let providerId: string;
export let params: object;
let formValues: object = {};
let originalfiles: Record<string, FileList> = {};
let files: Record<string, FileList> = {};
let updated = false;
let newMemberModal = false;
let message = '';
const { displayName } = getProviderDisplayNameAndIcon(provider);
switch (providerType) {
case MessagingProviderType.Push:
message = 'notifications';
break;
case MessagingProviderType.Sms:
message = 'SMS';
break;
case MessagingProviderType.Email:
message = 'emails';
break;
}
onMount(() => {
for (const input of inputs) {
const value = params[input.name];
formValues[input.name] = value;
if (input.type === 'file' && typeof value == 'string' && value.length > 0) {
const dataTransfer = new DataTransfer();
const f = new File([value], `${input.name}.${input.allowedFileExtensions}`);
dataTransfer.items.add(f);
files[input.name] = dataTransfer.files;
originalfiles[input.name] = dataTransfer.files;
}
}
});
async function update() {
try {
const promises = [];
for (const [key, value] of Object.entries(files)) {
const promise = value[0].text().then((text) => {
formValues[key] = text;
});
promises.push(promise);
}
await Promise.all(promises);
let response: Models.Provider;
switch (provider) {
case Providers.Twilio:
response = await sdk.forProject.messaging.updateTwilioProvider(
providerId,
undefined,
undefined,
formValues['accountSid'],
formValues['authToken'],
formValues['from']
);
break;
case Providers.Msg91:
response = await sdk.forProject.messaging.updateMsg91Provider(
providerId,
undefined,
undefined,
formValues['senderId'],
formValues['authKey'],
formValues['from']
);
break;
case Providers.Telesign:
response = await sdk.forProject.messaging.updateTelesignProvider(
providerId,
undefined,
undefined,
formValues['username'],
formValues['password'],
formValues['from']
);
break;
case Providers.Textmagic:
response = await sdk.forProject.messaging.updateTextmagicProvider(
providerId,
undefined,
undefined,
formValues['username'],
formValues['apiKey'],
formValues['from']
);
break;
case Providers.Vonage:
response = await sdk.forProject.messaging.updateVonageProvider(
providerId,
undefined,
undefined,
formValues['apiKey'],
formValues['apiSecret'],
formValues['from']
);
break;
case Providers.Mailgun:
response = await sdk.forProject.messaging.updateMailgunProvider(
providerId,
undefined,
formValues['apiKey'],
formValues['domain'],
formValues['isEuRegion'],
undefined,
formValues['fromName'] || undefined,
formValues['fromEmail'],
formValues['replyToName'] || undefined,
formValues['replyToEmail'] || undefined
);
break;
case Providers.Sendgrid:
response = await sdk.forProject.messaging.updateSendgridProvider(
providerId,
undefined,
undefined,
formValues['apiKey'],
formValues['fromName'] || undefined,
formValues['fromEmail'],
formValues['replyToName'] || undefined,
formValues['replyToEmail'] || undefined
);
break;
case Providers.SMTP:
response = await sdk.forProject.messaging.updateSMTPProvider(
providerId,
undefined,
formValues['host'],
formValues['port'] || undefined,
formValues['username'] || undefined,
formValues['password'] || undefined,
formValues['encryption'],
formValues['autoTLS'],
formValues['fromName'] || undefined,
formValues['fromEmail'],
formValues['replyToName'] || undefined,
formValues['replyToEmail'] || undefined,
undefined
);
break;
case Providers.FCM:
response = await sdk.forProject.messaging.updateFCMProvider(
providerId,
undefined,
undefined,
JSON.parse(formValues['serviceAccountJSON'])
);
break;
case Providers.APNS:
response = await sdk.forProject.messaging.updateAPNSProvider(
providerId,
undefined,
undefined,
formValues['authKey'],
formValues['authKeyId'],
formValues['teamId'],
formValues['bundleId']
);
break;
}
originalfiles = files;
await invalidate(Dependencies.MESSAGING_PROVIDER);
addNotification({
type: 'success',
message: `${response.name} has been updated`
});
trackEvent(Submit.MessagingProviderUpdate, {
provider: provider
});
} catch (error) {
addNotification({
type: 'error',
message: error.message
});
trackError(error, Submit.MessagingProviderUpdate);
}
}
$: updated =
inputs
.filter((input) => input.type !== 'file')
.some((input) => formValues[input.name] !== params[input.name]) ||
Object.keys(files).some((key) => files[key] !== originalfiles[key]);
</script>
<Form onSubmit={update}>
<CardGrid>
<div class="grid-1-2-col-1 u-flex u-cross-center u-gap-16" data-private>
<Heading tag="h6" size="7">Settings</Heading>
</div>
<p>
Configure the settings to <Button
link
href={`https://appwrite.io/docs/products/messaging/providers/${provider}`}
external>enable {displayName}</Button> to send {message}, or <Button
link
on:click={() => (newMemberModal = true)}>invite a team member</Button> to complete the
provider settings.
</p>
<svelte:fragment slot="aside">
<SettingsFormList bind:files {inputs} bind:params={formValues} />
</svelte:fragment>
<svelte:fragment slot="actions">
<div class="u-flex u-flex-wrap u-gap-12">
<Button disabled={!updated} submit>Update</Button>
</div>
</svelte:fragment>
</CardGrid>
</Form>
<CreateMember bind:showCreate={newMemberModal} />
@@ -5,16 +5,11 @@
import { Dependencies } from '$lib/constants';
import { Button, InputSwitch } from '$lib/elements/forms';
import { toLocaleDateTime } from '$lib/helpers/date';
import { isValueOfStringEnum } from '$lib/helpers/types';
import { addNotification } from '$lib/stores/notifications';
import { sdk } from '$lib/stores/sdk';
import { wizard } from '$lib/stores/wizard';
import { onMount } from 'svelte';
import { MessagingProviderType } from '@appwrite.io/console';
import Provider, { Providers } from '../../provider.svelte';
import ProviderType from '../../providerType.svelte';
import Update from '../update.svelte';
import { providerParams, providerType, provider } from '../store';
import { provider as providerData } from './store';
let enabled: boolean = null;
@@ -23,144 +18,6 @@
enabled ??= $providerData.enabled;
});
function configure() {
if (!isValueOfStringEnum(MessagingProviderType, $providerData.type)) {
throw new Error(`Invalid provider type: ${$providerData.type}`);
}
if (!isValueOfStringEnum(Providers, $providerData.provider)) {
throw new Error(`Invalid provider: ${$providerData.provider}`);
}
$providerType = $providerData.type;
$provider = $providerData.provider;
switch ($provider) {
case Providers.Twilio:
$providerParams[$provider] = {
providerId: $providerData.$id,
name: $providerData.name,
enabled: $providerData.enabled,
accountSid: $providerData.credentials['accountSid'],
authToken: $providerData.credentials['authToken'],
from: $providerData.options['from']
};
break;
case Providers.Msg91:
$providerParams[$provider] = {
providerId: $providerData.$id,
name: $providerData.name,
enabled: $providerData.enabled,
from: $providerData.options['from'],
senderId: $providerData.credentials['senderId'],
authKey: $providerData.credentials['authKey']
};
break;
case Providers.Telesign:
$providerParams[$provider] = {
providerId: $providerData.$id,
name: $providerData.name,
enabled: $providerData.enabled,
username: $providerData.credentials['username'],
password: $providerData.credentials['password'],
from: $providerData.options['from']
};
break;
case Providers.Textmagic:
$providerParams[$provider] = {
providerId: $providerData.$id,
name: $providerData.name,
enabled: $providerData.enabled,
username: $providerData.credentials['username'],
apiKey: $providerData.credentials['apiKey'],
from: $providerData.options['from']
};
break;
case Providers.Vonage:
$providerParams[$provider] = {
providerId: $providerData.$id,
name: $providerData.name,
enabled: $providerData.enabled,
apiKey: $providerData.credentials['apiKey'],
apiSecret: $providerData.credentials['apiSecret'],
from: $providerData.options['from']
};
break;
case Providers.Mailgun:
$providerParams[$provider] = {
providerId: $providerData.$id,
name: $providerData.name,
enabled: $providerData.enabled,
isEuRegion: false,
fromEmail: $providerData.options['fromEmail'],
fromName: $providerData.options['fromName'],
replyToEmail: $providerData.options['replyToEmail'],
replyToName: $providerData.options['replyToName'],
apiKey: $providerData.credentials['apiKey'],
domain: $providerData.credentials['domain']
};
break;
case Providers.Sendgrid:
$providerParams[$provider] = {
providerId: $providerData.$id,
name: $providerData.name,
enabled: $providerData.enabled,
apiKey: $providerData.credentials['apiKey'],
fromEmail: $providerData.options['fromEmail'],
fromName: $providerData.options['fromName'],
replyToEmail: $providerData.options['replyToEmail'],
replyToName: $providerData.options['replyToName']
};
break;
case Providers.SMTP:
$providerParams[$provider] = {
providerId: $providerData.$id,
name: $providerData.name,
enabled: $providerData.enabled,
host: $providerData.credentials['host'],
port: $providerData.credentials['port'],
username: $providerData.credentials['username'],
password: $providerData.credentials['password'],
fromName: $providerData.options['fromName'],
fromEmail: $providerData.options['fromEmail'],
replyToName: $providerData.options['replyToName'],
replyToEmail: $providerData.options['replyToEmail'],
encryption: $providerData.options['encryption'],
autoTLS: $providerData.options['autoTLS'],
mailer: $providerData.options['mailer']
};
break;
case Providers.FCM:
$providerParams[$provider] = {
providerId: $providerData.$id,
name: $providerData.name,
enabled: $providerData.enabled
};
if ('serviceAccountJSON' in $providerData.credentials) {
const serviceAccountJSON = $providerData.credentials['serviceAccountJSON'];
if (typeof serviceAccountJSON === 'string') {
$providerParams[$provider].serviceAccountJSON = serviceAccountJSON;
} else if (serviceAccountJSON instanceof Object) {
$providerParams[$provider].serviceAccountJSON =
JSON.stringify(serviceAccountJSON);
}
}
break;
case Providers.APNS:
$providerParams[$provider] = {
providerId: $providerData.$id,
name: $providerData.name,
enabled: $providerData.enabled,
authKey: $providerData.credentials['authKey'],
authKeyId: $providerData.credentials['authKeyId'],
teamId: $providerData.credentials['teamId'],
bundleId: $providerData.credentials['bundleId']
};
break;
}
wizard.start(Update);
}
async function updateStatus() {
try {
let response = { $id: '', name: '' };
@@ -292,7 +149,6 @@
<svelte:fragment slot="actions">
<div class="u-flex u-flex-wrap u-gap-12">
<Button secondary on:click={() => configure()}>Configure</Button>
<Button disabled={$providerData.enabled === enabled} on:click={() => updateStatus()}
>Update</Button>
</div>
@@ -1,163 +0,0 @@
<script lang="ts">
import { Wizard } from '$lib/layout';
import type { WizardStepsType } from '$lib/layout/wizard.svelte';
import Settings from './wizard/settings.svelte';
import { sdk } from '$lib/stores/sdk';
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { addNotification } from '$lib/stores/notifications';
import { goto, invalidate } from '$app/navigation';
import { base } from '$app/paths';
import { project } from '../../store';
import { wizard } from '$lib/stores/wizard';
import { provider, providerParams } from './store';
import { Providers } from '../provider.svelte';
import { Dependencies } from '$lib/constants';
import type { Models } from '@appwrite.io/console';
async function update() {
try {
let response: Models.Provider;
const providerId = $providerParams[$provider].providerId;
switch ($provider) {
case Providers.Twilio:
response = await sdk.forProject.messaging.updateTwilioProvider(
providerId,
$providerParams[$provider].name,
$providerParams[$provider].enabled,
$providerParams[$provider].accountSid,
$providerParams[$provider].authToken,
$providerParams[$provider].from
);
break;
case Providers.Msg91:
response = await sdk.forProject.messaging.updateMsg91Provider(
providerId,
$providerParams[$provider].name,
$providerParams[$provider].enabled,
$providerParams[$provider].senderId,
$providerParams[$provider].authKey,
$providerParams[$provider].from
);
break;
case Providers.Telesign:
response = await sdk.forProject.messaging.updateTelesignProvider(
providerId,
$providerParams[$provider].name,
$providerParams[$provider].enabled,
$providerParams[$provider].username,
$providerParams[$provider].password,
$providerParams[$provider].from
);
break;
case Providers.Textmagic:
response = await sdk.forProject.messaging.updateTextmagicProvider(
providerId,
$providerParams[$provider].name,
$providerParams[$provider].enabled,
$providerParams[$provider].username,
$providerParams[$provider].apiKey,
$providerParams[$provider].from
);
break;
case Providers.Vonage:
response = await sdk.forProject.messaging.updateVonageProvider(
providerId,
$providerParams[$provider].name,
$providerParams[$provider].enabled,
$providerParams[$provider].apiKey,
$providerParams[$provider].apiSecret,
$providerParams[$provider].from
);
break;
case Providers.Mailgun:
response = await sdk.forProject.messaging.updateMailgunProvider(
providerId,
$providerParams[$provider].name,
$providerParams[$provider].apiKey,
$providerParams[$provider].domain,
$providerParams[$provider].isEuRegion,
$providerParams[$provider].enabled,
$providerParams[$provider].fromName || undefined,
$providerParams[$provider].fromEmail,
$providerParams[$provider].replyToName || undefined,
$providerParams[$provider].replyToEmail || undefined
);
break;
case Providers.Sendgrid:
response = await sdk.forProject.messaging.updateSendgridProvider(
providerId,
$providerParams[$provider].name,
$providerParams[$provider].enabled,
$providerParams[$provider].apiKey,
$providerParams[$provider].fromName || undefined,
$providerParams[$provider].fromEmail,
$providerParams[$provider].replyToName || undefined,
$providerParams[$provider].replyToEmail || undefined
);
break;
case Providers.SMTP:
response = await sdk.forProject.messaging.updateSMTPProvider(
providerId,
$providerParams[$provider].name,
$providerParams[$provider].host,
$providerParams[$provider].port || undefined,
$providerParams[$provider].username || undefined,
$providerParams[$provider].password || undefined,
$providerParams[$provider].encryption,
$providerParams[$provider].autoTLS,
$providerParams[$provider].fromName || undefined,
$providerParams[$provider].fromEmail,
$providerParams[$provider].replyToName || undefined,
$providerParams[$provider].replyToEmail || undefined,
$providerParams[$provider].enabled
);
break;
case Providers.FCM:
response = await sdk.forProject.messaging.updateFCMProvider(
providerId,
$providerParams[$provider].name,
$providerParams[$provider].enabled,
JSON.parse($providerParams[$provider].serviceAccountJSON)
);
break;
case Providers.APNS:
response = await sdk.forProject.messaging.updateAPNSProvider(
providerId,
$providerParams[$provider].name,
$providerParams[$provider].enabled,
$providerParams[$provider].authKey,
$providerParams[$provider].authKeyId,
$providerParams[$provider].teamId,
$providerParams[$provider].bundleId
);
break;
}
await invalidate(Dependencies.MESSAGING_PROVIDER);
wizard.hide();
addNotification({
type: 'success',
message: `${response.name} has been updated`
});
trackEvent(Submit.MessagingProviderUpdate, {
provider: $provider
});
await goto(
`${base}/console/project-${$project.$id}/messaging/providers/provider-${response.$id}`
);
} catch (error) {
addNotification({
type: 'error',
message: error.message
});
trackError(error, Submit.MessagingProviderUpdate);
}
}
const stepsComponents: WizardStepsType = new Map();
stepsComponents.set(1, {
label: 'Settings',
component: Settings
});
</script>
<Wizard title="Update provider" steps={stepsComponents} on:finish={update} finalAction="Update" />