diff --git a/frontend/src/lib/components/ErrorOrRecoveryHandler.svelte b/frontend/src/lib/components/ErrorOrRecoveryHandler.svelte index aab564a4cf..c624b5a148 100644 --- a/frontend/src/lib/components/ErrorOrRecoveryHandler.svelte +++ b/frontend/src/lib/components/ErrorOrRecoveryHandler.svelte @@ -69,6 +69,8 @@ } from 'lucide-svelte' import { hubPaths } from '$lib/hub' import { isCloudHosted } from '$lib/cloud' + import { untrack } from 'svelte' + import { syncSlackBotArg } from './handlerSlackArg' import SmtpConfigurationStatus from './common/smtp/SmtpConfigurationStatus.svelte' import { SettingService } from '$lib/gen' import { isSmtpSettingsValid } from './instanceSettings/SmtpSettings.svelte' @@ -316,12 +318,13 @@ loadSmtpConfiguration() }) + let builtinArgs: Record | undefined $effect(() => { - if (handlerSelected === 'slack' && isSlackHandler(handlerPath)) { - handlerExtraArgs['slack'] = '$res:f/slack_bot/bot_token' - } else { - handlerExtraArgs['slack'] = undefined - } + const isSlackBuiltin = handlerSelected === 'slack' && isSlackHandler(handlerPath) + const args = handlerExtraArgs + // A replaced args object (Discard, reload) holds the stored config, not what the built-in set. + untrack(() => syncSlackBotArg(args, isSlackBuiltin, args === builtinArgs)) + builtinArgs = isSlackBuiltin ? args : undefined }) let lastHandlerSelected: ErrorHandler | undefined = $state(undefined) diff --git a/frontend/src/lib/components/handlerSlackArg.test.ts b/frontend/src/lib/components/handlerSlackArg.test.ts new file mode 100644 index 0000000000..b087a55f26 --- /dev/null +++ b/frontend/src/lib/components/handlerSlackArg.test.ts @@ -0,0 +1,21 @@ +import { describe, expect, it } from 'vitest' +import { syncSlackBotArg } from './handlerSlackArg' + +const AUTO = '$res:f/slack_bot/bot_token' + +describe('syncSlackBotArg', () => { + it("leaves a custom handler's slack value alone", () => { + const args = { slack: '$res:u/me/my_slack' } + syncSlackBotArg(args, false, false) + syncSlackBotArg(args, false, true) + expect(args).toEqual({ slack: '$res:u/me/my_slack' }) + }) + + it('sets the bot token for the built-in and drops it when leaving', () => { + const args: Record = {} + syncSlackBotArg(args, true, false) + expect(args.slack).toBe(AUTO) + syncSlackBotArg(args, false, true) + expect(args).toStrictEqual({}) + }) +}) diff --git a/frontend/src/lib/components/handlerSlackArg.ts b/frontend/src/lib/components/handlerSlackArg.ts new file mode 100644 index 0000000000..6b8e4d6f5c --- /dev/null +++ b/frontend/src/lib/components/handlerSlackArg.ts @@ -0,0 +1,17 @@ +import { WORKSPACE_SLACK_BOT_TOKEN_PATH } from '$lib/consts' + +const AUTO_SLACK_ARG = '$res:' + WORKSPACE_SLACK_BOT_TOKEN_PATH + +// `slack` belongs to the Slack built-in only while it holds the value the picker set; any other +// value is a custom handler's own argument and must survive handler switches and reloads. +export function syncSlackBotArg( + args: Record, + isSlackBuiltin: boolean, + wasSlackBuiltin: boolean +) { + if (isSlackBuiltin) { + args.slack = AUTO_SLACK_ARG + } else if (wasSlackBuiltin && args.slack === AUTO_SLACK_ARG) { + delete args.slack + } +} diff --git a/frontend/src/routes/(root)/(logged)/workspace_settings/+page.svelte b/frontend/src/routes/(root)/(logged)/workspace_settings/+page.svelte index 7228cf9f48..58f9d5d897 100644 --- a/frontend/src/routes/(root)/(logged)/workspace_settings/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/workspace_settings/+page.svelte @@ -1133,10 +1133,14 @@ } // Strip keys from extraArgs that are auto-managed by child components: - // - 'slack': computed by ErrorOrRecoveryHandler's $effect based on handler type + // - 'slack' while the Slack built-in is selected: set by ErrorOrRecoveryHandler's $effect // - 'channel_name': display metadata stripped by SchemaForm's removeExtraKey() - function normalizeHandlerExtraArgs(args: Record): Record { - const { slack: _, channel_name: __, ...rest } = args + function normalizeHandlerExtraArgs( + args: Record, + handlerSelected: ErrorHandler + ): Record { + const { channel_name: _, ...rest } = args + if (handlerSelected === 'slack') delete rest.slack return rest } @@ -1146,7 +1150,10 @@ errorHandlerSelected: initialErrorHandlerSelected, errorHandlerScriptPath: initialErrorHandlerScriptPath, errorHandlerItemKind: initialErrorHandlerItemKind, - errorHandlerExtraArgs: normalizeHandlerExtraArgs(initialErrorHandlerExtraArgs), + errorHandlerExtraArgs: normalizeHandlerExtraArgs( + initialErrorHandlerExtraArgs, + initialErrorHandlerSelected + ), errorHandlerMutedOnCancel: initialErrorHandlerMutedOnCancel, errorHandlerMutedOnUserPath: initialErrorHandlerMutedOnUserPath } @@ -1155,7 +1162,7 @@ errorHandlerSelected: errorHandlerSelected, errorHandlerScriptPath: errorHandlerScriptPath, errorHandlerItemKind: errorHandlerItemKind, - errorHandlerExtraArgs: normalizeHandlerExtraArgs(errorHandlerExtraArgs), + errorHandlerExtraArgs: normalizeHandlerExtraArgs(errorHandlerExtraArgs, errorHandlerSelected), errorHandlerMutedOnCancel: errorHandlerMutedOnCancel, errorHandlerMutedOnUserPath: errorHandlerMutedOnUserPath }