fix(ui): handle slack config snapshot

This commit is contained in:
Peter Steinberger
2026-01-06 01:16:25 +01:00
parent 5356adba8f
commit df9005d64c
3 changed files with 141 additions and 0 deletions

View File

@@ -35,6 +35,7 @@
- Heartbeat: make HEARTBEAT_OK ack padding configurable across heartbeat and cron delivery. (#238) — thanks @jalehman
- WhatsApp: set sender E.164 for direct chats so owner commands work in DMs.
- Slack: keep auto-replies in the original thread when responding to thread messages. Thanks @scald for PR #251.
- Control UI: avoid Slack config ReferenceError by reading slack config snapshots. Thanks @sreekaransrinath for PR #249.
### Maintenance
- Deps: bump pi-* stack, Slack SDK, discord-api-types, file-type, zod, and Biome.

View File

@@ -0,0 +1,139 @@
import { describe, expect, it } from "vitest";
import { applyConfigSnapshot, type ConfigState } from "./config";
import {
defaultDiscordActions,
defaultSlackActions,
type DiscordForm,
type IMessageForm,
type SignalForm,
type SlackForm,
type TelegramForm,
} from "../ui-types";
const baseTelegramForm: TelegramForm = {
token: "",
requireMention: true,
allowFrom: "",
proxy: "",
webhookUrl: "",
webhookSecret: "",
webhookPath: "",
};
const baseDiscordForm: DiscordForm = {
enabled: true,
token: "",
dmEnabled: true,
allowFrom: "",
groupEnabled: false,
groupChannels: "",
mediaMaxMb: "",
historyLimit: "",
textChunkLimit: "",
replyToMode: "off",
guilds: [],
actions: { ...defaultDiscordActions },
slashEnabled: false,
slashName: "",
slashSessionPrefix: "",
slashEphemeral: true,
};
const baseSlackForm: SlackForm = {
enabled: true,
botToken: "",
appToken: "",
dmEnabled: true,
allowFrom: "",
groupEnabled: false,
groupChannels: "",
mediaMaxMb: "",
textChunkLimit: "",
reactionNotifications: "own",
reactionAllowlist: "",
slashEnabled: false,
slashName: "",
slashSessionPrefix: "",
slashEphemeral: true,
actions: { ...defaultSlackActions },
channels: [],
};
const baseSignalForm: SignalForm = {
enabled: true,
account: "",
httpUrl: "",
httpHost: "",
httpPort: "",
cliPath: "",
autoStart: true,
receiveMode: "",
ignoreAttachments: false,
ignoreStories: false,
sendReadReceipts: false,
allowFrom: "",
mediaMaxMb: "",
};
const baseIMessageForm: IMessageForm = {
enabled: true,
cliPath: "",
dbPath: "",
service: "auto",
region: "",
allowFrom: "",
includeAttachments: false,
mediaMaxMb: "",
};
function createState(): ConfigState {
return {
client: null,
connected: false,
configLoading: false,
configRaw: "",
configValid: null,
configIssues: [],
configSaving: false,
configSnapshot: null,
configSchema: null,
configSchemaVersion: null,
configSchemaLoading: false,
configUiHints: {},
configForm: null,
configFormDirty: false,
configFormMode: "form",
lastError: null,
telegramForm: { ...baseTelegramForm },
discordForm: { ...baseDiscordForm },
slackForm: { ...baseSlackForm },
signalForm: { ...baseSignalForm },
imessageForm: { ...baseIMessageForm },
telegramConfigStatus: null,
discordConfigStatus: null,
slackConfigStatus: null,
signalConfigStatus: null,
imessageConfigStatus: null,
};
}
describe("applyConfigSnapshot", () => {
it("handles missing slack config without throwing", () => {
const state = createState();
applyConfigSnapshot(state, {
config: {
telegram: {},
discord: {},
signal: {},
imessage: {},
},
valid: true,
issues: [],
raw: "{}",
});
expect(state.slackForm.botToken).toBe("");
expect(state.slackForm.actions).toEqual(defaultSlackActions);
});
});

View File

@@ -100,6 +100,7 @@ export function applyConfigSnapshot(state: ConfigState, snapshot: ConfigSnapshot
const config = snapshot.config ?? {};
const telegram = (config.telegram ?? {}) as Record<string, unknown>;
const discord = (config.discord ?? {}) as Record<string, unknown>;
const slack = (config.slack ?? {}) as Record<string, unknown>;
const signal = (config.signal ?? {}) as Record<string, unknown>;
const imessage = (config.imessage ?? {}) as Record<string, unknown>;
const toList = (value: unknown) =>