Running
diff --git a/ui/src/ui/views/channels.shared.test.ts b/ui/src/ui/views/channels.shared.test.ts
new file mode 100644
index 00000000000..ce879c147b3
--- /dev/null
+++ b/ui/src/ui/views/channels.shared.test.ts
@@ -0,0 +1,88 @@
+import { describe, expect, it } from "vitest";
+import { resolveChannelConfigured } from "./channels.shared.ts";
+import type { ChannelsProps } from "./channels.types.ts";
+
+function createProps(snapshot: ChannelsProps["snapshot"]): ChannelsProps {
+ return {
+ connected: true,
+ loading: false,
+ snapshot,
+ lastError: null,
+ lastSuccessAt: null,
+ whatsappMessage: null,
+ whatsappQrDataUrl: null,
+ whatsappConnected: null,
+ whatsappBusy: false,
+ configSchema: null,
+ configSchemaLoading: false,
+ configForm: null,
+ configUiHints: {},
+ configSaving: false,
+ configFormDirty: false,
+ nostrProfileFormState: null,
+ nostrProfileAccountId: null,
+ onRefresh: () => {},
+ onWhatsAppStart: () => {},
+ onWhatsAppWait: () => {},
+ onWhatsAppLogout: () => {},
+ onConfigPatch: () => {},
+ onConfigSave: () => {},
+ onConfigReload: () => {},
+ onNostrProfileEdit: () => {},
+ onNostrProfileCancel: () => {},
+ onNostrProfileFieldChange: () => {},
+ onNostrProfileSave: () => {},
+ onNostrProfileImport: () => {},
+ onNostrProfileToggleAdvanced: () => {},
+ };
+}
+
+describe("resolveChannelConfigured", () => {
+ it("returns the channel summary configured flag when present", () => {
+ const props = createProps({
+ ts: Date.now(),
+ channelOrder: ["discord"],
+ channelLabels: { discord: "Discord" },
+ channels: { discord: { configured: false } },
+ channelAccounts: {
+ discord: [{ accountId: "discord-main", configured: true }],
+ },
+ channelDefaultAccountId: { discord: "discord-main" },
+ });
+
+ expect(resolveChannelConfigured("discord", props)).toBe(false);
+ });
+
+ it("falls back to the default account when the channel summary omits configured", () => {
+ const props = createProps({
+ ts: Date.now(),
+ channelOrder: ["discord"],
+ channelLabels: { discord: "Discord" },
+ channels: { discord: { running: true } },
+ channelAccounts: {
+ discord: [
+ { accountId: "default", configured: false },
+ { accountId: "discord-main", configured: true },
+ ],
+ },
+ channelDefaultAccountId: { discord: "discord-main" },
+ });
+
+ expect(resolveChannelConfigured("discord", props)).toBe(true);
+ });
+
+ it("falls back to the first account when no default account id is available", () => {
+ const props = createProps({
+ ts: Date.now(),
+ channelOrder: ["slack"],
+ channelLabels: { slack: "Slack" },
+ channels: { slack: { running: true } },
+ channelAccounts: {
+ slack: [{ accountId: "workspace-a", configured: true }],
+ },
+ channelDefaultAccountId: {},
+ });
+
+ expect(resolveChannelConfigured("slack", props)).toBe(true);
+ });
+});
diff --git a/ui/src/ui/views/channels.shared.ts b/ui/src/ui/views/channels.shared.ts
index 7481daf91cc..5bd5755ea1c 100644
--- a/ui/src/ui/views/channels.shared.ts
+++ b/ui/src/ui/views/channels.shared.ts
@@ -19,6 +19,28 @@ export function channelEnabled(key: ChannelKey, props: ChannelsProps) {
return configured || running || connected || accountActive;
}
+export function resolveChannelConfigured(key: ChannelKey, props: ChannelsProps): boolean | null {
+ const snapshot = props.snapshot;
+ const channels = snapshot?.channels as Record
| null;
+ const channelStatus = channels?.[key] as Record | undefined;
+ if (typeof channelStatus?.configured === "boolean") {
+ return channelStatus.configured;
+ }
+
+ const accounts = snapshot?.channelAccounts?.[key] ?? [];
+ const defaultAccountId = snapshot?.channelDefaultAccountId?.[key];
+ const defaultAccount =
+ (defaultAccountId
+ ? accounts.find((account) => account.accountId === defaultAccountId)
+ : undefined) ?? accounts[0];
+
+ if (typeof defaultAccount?.configured === "boolean") {
+ return defaultAccount.configured;
+ }
+
+ return null;
+}
+
export function getChannelAccountCount(
key: ChannelKey,
channelAccounts?: Record | null,
diff --git a/ui/src/ui/views/channels.signal.ts b/ui/src/ui/views/channels.signal.ts
index db7d9a52de6..06417277223 100644
--- a/ui/src/ui/views/channels.signal.ts
+++ b/ui/src/ui/views/channels.signal.ts
@@ -2,6 +2,7 @@ import { html, nothing } from "lit";
import { formatRelativeTimestamp } from "../format.ts";
import type { SignalStatus } from "../types.ts";
import { renderChannelConfigSection } from "./channels.config.ts";
+import { resolveChannelConfigured } from "./channels.shared.ts";
import type { ChannelsProps } from "./channels.types.ts";
export function renderSignalCard(params: {
@@ -10,6 +11,7 @@ export function renderSignalCard(params: {
accountCountLabel: unknown;
}) {
const { props, signal, accountCountLabel } = params;
+ const configured = resolveChannelConfigured("signal", props);
return html`
@@ -20,7 +22,7 @@ export function renderSignalCard(params: {
Configured
- ${signal?.configured ? "Yes" : "No"}
+ ${configured == null ? "n/a" : configured ? "Yes" : "No"}
Running
diff --git a/ui/src/ui/views/channels.slack.ts b/ui/src/ui/views/channels.slack.ts
index ca53e3e2d7b..1cf8c2a3d81 100644
--- a/ui/src/ui/views/channels.slack.ts
+++ b/ui/src/ui/views/channels.slack.ts
@@ -2,6 +2,7 @@ import { html, nothing } from "lit";
import { formatRelativeTimestamp } from "../format.ts";
import type { SlackStatus } from "../types.ts";
import { renderChannelConfigSection } from "./channels.config.ts";
+import { resolveChannelConfigured } from "./channels.shared.ts";
import type { ChannelsProps } from "./channels.types.ts";
export function renderSlackCard(params: {
@@ -10,6 +11,7 @@ export function renderSlackCard(params: {
accountCountLabel: unknown;
}) {
const { props, slack, accountCountLabel } = params;
+ const configured = resolveChannelConfigured("slack", props);
return html`
@@ -20,7 +22,7 @@ export function renderSlackCard(params: {
Configured
- ${slack?.configured ? "Yes" : "No"}
+ ${configured == null ? "n/a" : configured ? "Yes" : "No"}
Running
diff --git a/ui/src/ui/views/channels.telegram.ts b/ui/src/ui/views/channels.telegram.ts
index 96381a62890..9c63f44dd54 100644
--- a/ui/src/ui/views/channels.telegram.ts
+++ b/ui/src/ui/views/channels.telegram.ts
@@ -2,6 +2,7 @@ import { html, nothing } from "lit";
import { formatRelativeTimestamp } from "../format.ts";
import type { ChannelAccountSnapshot, TelegramStatus } from "../types.ts";
import { renderChannelConfigSection } from "./channels.config.ts";
+import { resolveChannelConfigured } from "./channels.shared.ts";
import type { ChannelsProps } from "./channels.types.ts";
export function renderTelegramCard(params: {
@@ -12,6 +13,7 @@ export function renderTelegramCard(params: {
}) {
const { props, telegram, telegramAccounts, accountCountLabel } = params;
const hasMultipleAccounts = telegramAccounts.length > 1;
+ const configured = resolveChannelConfigured("telegram", props);
const renderAccountCard = (account: ChannelAccountSnapshot) => {
const probe = account.probe as { bot?: { username?: string } } | undefined;
@@ -69,7 +71,7 @@ export function renderTelegramCard(params: {
Configured
- ${telegram?.configured ? "Yes" : "No"}
+ ${configured == null ? "n/a" : configured ? "Yes" : "No"}
Running
diff --git a/ui/src/ui/views/channels.ts b/ui/src/ui/views/channels.ts
index 89062891773..8b6ee7cdd9d 100644
--- a/ui/src/ui/views/channels.ts
+++ b/ui/src/ui/views/channels.ts
@@ -19,7 +19,11 @@ import { renderDiscordCard } from "./channels.discord.ts";
import { renderGoogleChatCard } from "./channels.googlechat.ts";
import { renderIMessageCard } from "./channels.imessage.ts";
import { renderNostrCard } from "./channels.nostr.ts";
-import { channelEnabled, renderChannelAccountCount } from "./channels.shared.ts";
+import {
+ channelEnabled,
+ renderChannelAccountCount,
+ resolveChannelConfigured,
+} from "./channels.shared.ts";
import { renderSignalCard } from "./channels.signal.ts";
import { renderSlackCard } from "./channels.slack.ts";
import { renderTelegramCard } from "./channels.telegram.ts";
@@ -184,7 +188,7 @@ function renderGenericChannelCard(
) {
const label = resolveChannelLabel(props.snapshot, key);
const status = props.snapshot?.channels?.[key] as Record
| undefined;
- const configured = typeof status?.configured === "boolean" ? status.configured : undefined;
+ const configured = resolveChannelConfigured(key, props);
const running = typeof status?.running === "boolean" ? status.running : undefined;
const connected = typeof status?.connected === "boolean" ? status.connected : undefined;
const lastError = typeof status?.lastError === "string" ? status.lastError : undefined;
diff --git a/ui/src/ui/views/channels.whatsapp.ts b/ui/src/ui/views/channels.whatsapp.ts
index 463788c1f6c..d6e0a90e1e8 100644
--- a/ui/src/ui/views/channels.whatsapp.ts
+++ b/ui/src/ui/views/channels.whatsapp.ts
@@ -2,6 +2,7 @@ import { html, nothing } from "lit";
import { formatRelativeTimestamp, formatDurationHuman } from "../format.ts";
import type { WhatsAppStatus } from "../types.ts";
import { renderChannelConfigSection } from "./channels.config.ts";
+import { resolveChannelConfigured } from "./channels.shared.ts";
import type { ChannelsProps } from "./channels.types.ts";
export function renderWhatsAppCard(params: {
@@ -10,6 +11,7 @@ export function renderWhatsAppCard(params: {
accountCountLabel: unknown;
}) {
const { props, whatsapp, accountCountLabel } = params;
+ const configured = resolveChannelConfigured("whatsapp", props);
return html`
@@ -20,7 +22,7 @@ export function renderWhatsAppCard(params: {
Configured
- ${whatsapp?.configured ? "Yes" : "No"}
+ ${configured == null ? "n/a" : configured ? "Yes" : "No"}
Linked