refactor(i18n): share integration status text between web and native

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
jakeandClaude Opus 5.5 committed 2026-10-05 04:26:05 -04:00
1 parent 4adaa4a73b
commit 01a14157c6
4 files changed
+30 -46

No files matched your search

@@ -10,7 +10,7 @@ import {
SonarrIcon,
} from "@/components/settings/icons";
import { getServerUrl } from "@/lib/server";
import { formatRelativeTime } from "@sofa/i18n/format";
import { listStatus, webhookStatus } from "@sofa/i18n/integration-status";
export interface IntegrationConfig {
provider: "plex" | "jellyfin" | "emby" | "sonarr" | "radarr";
@@ -24,22 +24,6 @@ export interface IntegrationConfig {
setupSteps: string[];
}
function webhookStatus(i18n: I18n, lastEventAt: string | null): string {
if (lastEventAt) {
const timeAgo = formatRelativeTime(new Date(lastEventAt));
return i18n._(msg`Last event ${timeAgo}`);
}
return i18n._(msg`Ready — nothing received yet`);
}
function listStatus(i18n: I18n, lastEventAt: string | null): string {
if (lastEventAt) {
const timeAgo = formatRelativeTime(new Date(lastEventAt));
return i18n._(msg`Last polled ${timeAgo}`);
}
return i18n._(msg`Ready — not polled yet`);
}
export function getIntegrationConfigs(i18n: I18n): IntegrationConfig[] {
return [
{
@@ -49,7 +33,7 @@ export function getIntegrationConfigs(i18n: I18n): IntegrationConfig[] {
type: "webhook",
buildUrl: (token) => `${getServerUrl()}/api/webhooks/${token}`,
urlLabel: i18n._(msg`Webhook URL`),
connectedStatus: (lastEventAt) => webhookStatus(i18n, lastEventAt),
connectedStatus: (lastEventAt) => webhookStatus(lastEventAt, i18n),
requirementNote: i18n._(msg`Requires an active Plex Pass subscription.`),
setupSteps: [
i18n._(msg`Open Plex, go to Settings > Webhooks`),
@@ -64,7 +48,7 @@ export function getIntegrationConfigs(i18n: I18n): IntegrationConfig[] {
type: "webhook",
buildUrl: (token) => `${getServerUrl()}/api/webhooks/${token}`,
urlLabel: i18n._(msg`Webhook URL`),
connectedStatus: (lastEventAt) => webhookStatus(i18n, lastEventAt),
connectedStatus: (lastEventAt) => webhookStatus(lastEventAt, i18n),
setupSteps: [
i18n._(msg`Install the Webhook plugin from Jellyfin's plugin catalog`),
i18n._(msg`Go to Dashboard > Plugins > Webhook`),
@@ -80,7 +64,7 @@ export function getIntegrationConfigs(i18n: I18n): IntegrationConfig[] {
type: "webhook",
buildUrl: (token) => `${getServerUrl()}/api/webhooks/${token}`,
urlLabel: i18n._(msg`Webhook URL`),
connectedStatus: (lastEventAt) => webhookStatus(i18n, lastEventAt),
connectedStatus: (lastEventAt) => webhookStatus(lastEventAt, i18n),
requirementNote: i18n._(
msg`Requires Emby Server 4.7.9+ and an active Emby Premiere license.`,
),
@@ -98,7 +82,7 @@ export function getIntegrationConfigs(i18n: I18n): IntegrationConfig[] {
type: "list",
buildUrl: (token) => `${getServerUrl()}/api/lists/${token}`,
urlLabel: i18n._(msg`Sonarr List URL`),
connectedStatus: (lastEventAt) => listStatus(i18n, lastEventAt),
connectedStatus: (lastEventAt) => listStatus(lastEventAt, i18n),
setupSteps: [
i18n._(msg`Open Sonarr, go to Settings > Import Lists`),
i18n._(msg`Click "+" and select "Custom Lists"`),
@@ -116,7 +100,7 @@ export function getIntegrationConfigs(i18n: I18n): IntegrationConfig[] {
type: "list",
buildUrl: (token) => `${getServerUrl()}/api/lists/${token}`,
urlLabel: i18n._(msg`Radarr List URL`),
connectedStatus: (lastEventAt) => listStatus(i18n, lastEventAt),
connectedStatus: (lastEventAt) => listStatus(lastEventAt, i18n),
setupSteps: [
i18n._(msg`Open Radarr, go to Settings > Import Lists`),
i18n._(msg`Click "+" and select "Custom Lists"`),
@@ -1,4 +1,3 @@
import { msg } from "@lingui/core/macro";
import { Trans, useLingui } from "@lingui/react/macro";
import {
IconBook2,
@@ -27,8 +26,6 @@ import {
import { Label } from "@/components/ui/label";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { orpc } from "@/lib/orpc/client";
import { i18n } from "@sofa/i18n";
import { formatRelativeTime } from "@sofa/i18n/format";
// ─── Types ──────────────────────────────────────────────────────────
@@ -280,23 +277,3 @@ export function IntegrationCard({
</Card>
);
}
// ─── Helpers for config authoring ───────────────────────────────────
/** Status line for webhook integrations (shows last event time). */
export function webhookStatus(lastEventAt: string | null): string {
if (lastEventAt) {
const relativeTime = formatRelativeTime(lastEventAt);
return i18n._(msg`Last event ${relativeTime}`);
}
return i18n._(msg`Ready — nothing received yet`);
}
/** Status line for list integrations (shows last event time). */
export function listStatus(lastEventAt: string | null): string {
if (lastEventAt) {
const relativeTime = formatRelativeTime(lastEventAt);
return i18n._(msg`Last polled ${relativeTime}`);
}
return i18n._(msg`Ready — not polled yet`);
}
@@ -2,10 +2,10 @@ import { Trans } from "@lingui/react/macro";
import { IconExternalLink, IconInfoCircle } from "@tabler/icons-react";
import { Alert, AlertDescription } from "@/components/ui/alert";
import { listStatus, webhookStatus } from "@sofa/i18n/integration-status";
import { EmbyIcon, JellyfinIcon, PlexIcon, RadarrIcon, SonarrIcon } from "./icons";
import type { IntegrationConfig } from "./integration-card";
import { listStatus, webhookStatus } from "./integration-card";
function origin() {
return typeof window !== "undefined" ? window.location.origin : "";
+23
View File
@@ -0,0 +1,23 @@
import type { I18n } from "@lingui/core";
import { msg } from "@lingui/core/macro";
import { formatRelativeTime } from "./format";
import { i18n as defaultI18n } from "./index";
/** Status line for webhook integrations (last event time). */
export function webhookStatus(lastEventAt: string | null, i18n: I18n = defaultI18n): string {
if (lastEventAt) {
const relativeTime = formatRelativeTime(lastEventAt);
return i18n._(msg`Last event ${relativeTime}`);
}
return i18n._(msg`Ready — nothing received yet`);
}
/** Status line for list integrations (last poll time). */
export function listStatus(lastEventAt: string | null, i18n: I18n = defaultI18n): string {
if (lastEventAt) {
const relativeTime = formatRelativeTime(lastEventAt);
return i18n._(msg`Last polled ${relativeTime}`);
}
return i18n._(msg`Ready — not polled yet`);
}