UI: derive channel configured state from default account
This commit is contained in:
parent
c7134e629c
commit
13c657182a
@ -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`
|
||||
<div class="card">
|
||||
@ -20,7 +22,7 @@ export function renderDiscordCard(params: {
|
||||
<div class="status-list" style="margin-top: 16px;">
|
||||
<div>
|
||||
<span class="label">Configured</span>
|
||||
<span>${discord?.configured ? "Yes" : "No"}</span>
|
||||
<span>${configured == null ? "n/a" : configured ? "Yes" : "No"}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="label">Running</span>
|
||||
|
||||
@ -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`
|
||||
<div class="card">
|
||||
@ -20,7 +22,7 @@ export function renderGoogleChatCard(params: {
|
||||
<div class="status-list" style="margin-top: 16px;">
|
||||
<div>
|
||||
<span class="label">Configured</span>
|
||||
<span>${googleChat ? (googleChat.configured ? "Yes" : "No") : "n/a"}</span>
|
||||
<span>${configured == null ? "n/a" : configured ? "Yes" : "No"}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="label">Running</span>
|
||||
|
||||
@ -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`
|
||||
<div class="card">
|
||||
@ -20,7 +22,7 @@ export function renderIMessageCard(params: {
|
||||
<div class="status-list" style="margin-top: 16px;">
|
||||
<div>
|
||||
<span class="label">Configured</span>
|
||||
<span>${imessage?.configured ? "Yes" : "No"}</span>
|
||||
<span>${configured == null ? "n/a" : configured ? "Yes" : "No"}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="label">Running</span>
|
||||
|
||||
88
ui/src/ui/views/channels.shared.test.ts
Normal file
88
ui/src/ui/views/channels.shared.test.ts
Normal file
@ -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);
|
||||
});
|
||||
});
|
||||
@ -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<string, unknown> | null;
|
||||
const channelStatus = channels?.[key] as Record<string, unknown> | 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<string, ChannelAccountSnapshot[]> | null,
|
||||
|
||||
@ -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`
|
||||
<div class="card">
|
||||
@ -20,7 +22,7 @@ export function renderSignalCard(params: {
|
||||
<div class="status-list" style="margin-top: 16px;">
|
||||
<div>
|
||||
<span class="label">Configured</span>
|
||||
<span>${signal?.configured ? "Yes" : "No"}</span>
|
||||
<span>${configured == null ? "n/a" : configured ? "Yes" : "No"}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="label">Running</span>
|
||||
|
||||
@ -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`
|
||||
<div class="card">
|
||||
@ -20,7 +22,7 @@ export function renderSlackCard(params: {
|
||||
<div class="status-list" style="margin-top: 16px;">
|
||||
<div>
|
||||
<span class="label">Configured</span>
|
||||
<span>${slack?.configured ? "Yes" : "No"}</span>
|
||||
<span>${configured == null ? "n/a" : configured ? "Yes" : "No"}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="label">Running</span>
|
||||
|
||||
@ -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: {
|
||||
<div class="status-list" style="margin-top: 16px;">
|
||||
<div>
|
||||
<span class="label">Configured</span>
|
||||
<span>${telegram?.configured ? "Yes" : "No"}</span>
|
||||
<span>${configured == null ? "n/a" : configured ? "Yes" : "No"}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="label">Running</span>
|
||||
|
||||
@ -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<string, unknown> | 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;
|
||||
|
||||
@ -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`
|
||||
<div class="card">
|
||||
@ -20,7 +22,7 @@ export function renderWhatsAppCard(params: {
|
||||
<div class="status-list" style="margin-top: 16px;">
|
||||
<div>
|
||||
<span class="label">Configured</span>
|
||||
<span>${whatsapp?.configured ? "Yes" : "No"}</span>
|
||||
<span>${configured == null ? "n/a" : configured ? "Yes" : "No"}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="label">Linked</span>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user