diff --git a/ui/src/ui/views/channels.discord.ts b/ui/src/ui/views/channels.discord.ts index 4da44152fc7..bc921f06888 100644 --- a/ui/src/ui/views/channels.discord.ts +++ b/ui/src/ui/views/channels.discord.ts @@ -2,6 +2,7 @@ import { html, nothing } from "lit"; import { formatRelativeTimestamp } from "../format.ts"; import type { DiscordStatus } from "../types.ts"; import { renderChannelConfigSection } from "./channels.config.ts"; +import { resolveChannelConfigured } from "./channels.shared.ts"; import type { ChannelsProps } from "./channels.types.ts"; export function renderDiscordCard(params: { @@ -10,6 +11,7 @@ export function renderDiscordCard(params: { accountCountLabel: unknown; }) { const { props, discord, accountCountLabel } = params; + const configured = resolveChannelConfigured("discord", props); return html`
@@ -20,7 +22,7 @@ export function renderDiscordCard(params: {
Configured - ${discord?.configured ? "Yes" : "No"} + ${configured == null ? "n/a" : configured ? "Yes" : "No"}
Running diff --git a/ui/src/ui/views/channels.googlechat.ts b/ui/src/ui/views/channels.googlechat.ts index ee9234bb0c3..6a867af449e 100644 --- a/ui/src/ui/views/channels.googlechat.ts +++ b/ui/src/ui/views/channels.googlechat.ts @@ -2,6 +2,7 @@ import { html, nothing } from "lit"; import { formatRelativeTimestamp } from "../format.ts"; import type { GoogleChatStatus } from "../types.ts"; import { renderChannelConfigSection } from "./channels.config.ts"; +import { resolveChannelConfigured } from "./channels.shared.ts"; import type { ChannelsProps } from "./channels.types.ts"; export function renderGoogleChatCard(params: { @@ -10,6 +11,7 @@ export function renderGoogleChatCard(params: { accountCountLabel: unknown; }) { const { props, googleChat, accountCountLabel } = params; + const configured = resolveChannelConfigured("googlechat", props); return html`
@@ -20,7 +22,7 @@ export function renderGoogleChatCard(params: {
Configured - ${googleChat ? (googleChat.configured ? "Yes" : "No") : "n/a"} + ${configured == null ? "n/a" : configured ? "Yes" : "No"}
Running diff --git a/ui/src/ui/views/channels.imessage.ts b/ui/src/ui/views/channels.imessage.ts index f474b4e9cd5..18f84a8b7b4 100644 --- a/ui/src/ui/views/channels.imessage.ts +++ b/ui/src/ui/views/channels.imessage.ts @@ -2,6 +2,7 @@ import { html, nothing } from "lit"; import { formatRelativeTimestamp } from "../format.ts"; import type { IMessageStatus } from "../types.ts"; import { renderChannelConfigSection } from "./channels.config.ts"; +import { resolveChannelConfigured } from "./channels.shared.ts"; import type { ChannelsProps } from "./channels.types.ts"; export function renderIMessageCard(params: { @@ -10,6 +11,7 @@ export function renderIMessageCard(params: { accountCountLabel: unknown; }) { const { props, imessage, accountCountLabel } = params; + const configured = resolveChannelConfigured("imessage", props); return html`
@@ -20,7 +22,7 @@ export function renderIMessageCard(params: {
Configured - ${imessage?.configured ? "Yes" : "No"} + ${configured == null ? "n/a" : configured ? "Yes" : "No"}
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