diff --git a/frontend/src/lib/components/workspaceSettings/WorkspaceIntegrations.svelte b/frontend/src/lib/components/workspaceSettings/WorkspaceIntegrations.svelte index ecda4adc08..0ed0c706a9 100644 --- a/frontend/src/lib/components/workspaceSettings/WorkspaceIntegrations.svelte +++ b/frontend/src/lib/components/workspaceSettings/WorkspaceIntegrations.svelte @@ -4,7 +4,7 @@ import { Button, Alert } from '$lib/components/common' import Skeleton from '$lib/components/common/skeleton/Skeleton.svelte' import SettingsPageHeader from '$lib/components/settings/SettingsPageHeader.svelte' - import { ExternalLink, KeyRound, Pencil, Trash2 } from 'lucide-svelte' + import { Check, ExternalLink, KeyRound, Pencil, Trash2 } from 'lucide-svelte' import DropdownV2 from '$lib/components/DropdownV2.svelte' import type { Item } from '$lib/utils' import NextcloudIcon from '$lib/components/icons/NextcloudIcon.svelte' @@ -19,7 +19,7 @@ import ConfirmationModal from '../common/confirmationModal/ConfirmationModal.svelte' import { createAsyncConfirmationModal } from '../common/confirmationModal/asyncConfirmationModal.svelte' import NativeConnectionPicker from '$lib/components/triggers/native/NativeConnectionPicker.svelte' - import { nativeOAuthRedirectUri } from '$lib/components/triggers/native/utils' + import { connectNativeAccount, nativeOAuthRedirectUri } from '$lib/components/triggers/native/utils' interface WorkspaceIntegration { service_name: string @@ -95,6 +95,9 @@ let connections = $state>({}) let loading = $state(false) // The service whose OAuth app dialog is open. + // Services whose shared account picker is open. + let editingShared = $state>({}) + let connectingShared = $state(null) let configOpen = $state>( Object.fromEntries(Object.keys(supportedServices).map((s) => [s, false])) ) @@ -157,8 +160,8 @@ } } - async function setWorkspaceConnection(serviceName: string, path: string | undefined) { - if (!$workspaceStore) return + async function setWorkspaceConnection(serviceName: string, path: string | undefined): Promise { + if (!$workspaceStore) return false const displayName = supportedServices[serviceName]?.displayName ?? serviceName if (path) { @@ -169,14 +172,14 @@ confirmationText: 'Share', children: `Every member will be able to create and edit ${displayName} triggers that act as ${account}. They will not see its token. It replaces any connection shared before.` }) - if (!confirmed) return + if (!confirmed) return false } else { const confirmed = await confirmationModal.ask({ title: 'Stop sharing this account?', confirmationText: 'Stop sharing', children: `Members will no longer be able to create or edit ${displayName} triggers through it, unless they can read it. Its triggers keep running.` }) - if (!confirmed) return + if (!confirmed) return false } try { @@ -187,11 +190,29 @@ }) sendUserToast(path ? `${path} is now the shared account` : 'Stopped sharing') await loadConnections(serviceName) + return true } catch (err: any) { sendUserToast(`Failed to update the shared account: ${err.body ?? err.message}`, true) + return false } } + async function connectAndShare(serviceName: string) { + if (!$workspaceStore) return + connectingShared = serviceName + try { + const path = await connectNativeAccount($workspaceStore, serviceName as NativeServiceName) + await loadConnections(serviceName) + if (await setWorkspaceConnection(serviceName, path)) editingShared[serviceName] = false + } catch (err: any) { + const displayName = supportedServices[serviceName]?.displayName ?? serviceName + sendUserToast(`Could not connect ${displayName}: ${err.body ?? err.message}`, true) + } finally { + connectingShared = null + } + } + + function docsItem(config: ServiceConfig): Item[] { return config.docsUrl ? [{ displayName: 'Docs', icon: ExternalLink, href: config.docsUrl, hrefTarget: '_blank' }] @@ -362,22 +383,80 @@ {/if} - {#if workspaceApp || usesInstanceApp} -
-
Shared account
-
- Members can create triggers as this account without seeing its token. + {#if !workspaceApp && !usesInstanceApp} +
+ Members can't create {config.displayName} triggers yet. +
+ {:else} + {#if serviceName === 'nextcloud' && !workspaceConnection} +
+
+ Nextcloud only accepts webhooks from an admin account. Connect one and share it so + members can create triggers. +
+
- setWorkspaceConnection(serviceName, path)} - onConnectionsChange={() => loadConnections(serviceName)} - /> + {:else} +
+ + {serviceName === 'nextcloud' + ? 'Members can create triggers through the shared account.' + : `Members can connect their own ${config.displayName} account when they create a trigger.`} +
+ {/if} + +
+
+ Shared account + {#if workspaceConnection} + {workspaceConnection} + {:else} + None + {/if} + {#if !editingShared[serviceName]} + + {/if} +
+ {#if editingShared[serviceName]} +
+ Members can create triggers as this account without seeing its token. +
+ + {#key workspaceConnection} + { + if (await setWorkspaceConnection(serviceName, path)) { + editingShared[serviceName] = false + } + }} + onConnectionsChange={() => loadConnections(serviceName)} + /> + {/key} + + {/if}
{/if}