feat(settings): label provider order with account modes
This commit is contained in:
+20
-9
@@ -975,15 +975,26 @@ const KNOWN_HOSTERS: { id: string; label: string }[] = [
|
||||
{ id: "isra", label: "Isra.cloud" }
|
||||
];
|
||||
|
||||
export function buildProviderOrderEntry(provider: DebridProvider, settings: RendererSettings): {
|
||||
id: DebridProvider;
|
||||
label: string;
|
||||
icon: string;
|
||||
} {
|
||||
const legacyMegaService = settings.megaDebridPreferApi ? "megadebrid-api" : "megadebrid-web";
|
||||
const service = provider === "megadebrid" ? legacyMegaService : provider as AccountService;
|
||||
const kind = getConfiguredAccountKind(settings, service);
|
||||
const option = kind ? ACCOUNT_OPTIONS.find((entry) => entry.kind === kind) : null;
|
||||
const modeLabel = option?.modeLabel || (provider === "megadebrid" ? (settings.megaDebridPreferApi ? "API" : "Web-Login") : "");
|
||||
const serviceLabel = option?.serviceLabel || providerLabels[provider];
|
||||
return {
|
||||
id: provider,
|
||||
label: modeLabel ? `${serviceLabel} (${modeLabel})` : serviceLabel,
|
||||
icon: ACCOUNT_SERVICE_ICONS[option?.service || service]
|
||||
};
|
||||
}
|
||||
|
||||
function providerLabelWithMode(provider: DebridProvider, settings: RendererSettings): string {
|
||||
const base = providerLabels[provider];
|
||||
if (provider === "megadebrid" || provider === "megadebrid-api" || provider === "megadebrid-web") {
|
||||
return base;
|
||||
}
|
||||
const kind = getConfiguredAccountKind(settings, provider as AccountService);
|
||||
if (!kind) return base;
|
||||
const opt = ACCOUNT_OPTIONS.find((o) => o.kind === kind);
|
||||
return opt?.modeLabel ? `${base} (${opt.modeLabel})` : base;
|
||||
return buildProviderOrderEntry(provider, settings).label;
|
||||
}
|
||||
|
||||
function formatAllDebridSourceLabel(source: AllDebridHostInfo["source"]): string {
|
||||
@@ -5586,7 +5597,7 @@ export function App(): ReactElement {
|
||||
statusSort: accountStatusSort,
|
||||
runtime: accountRuntimeModel,
|
||||
rules: {
|
||||
providerOrder: activeProviderOrder.map((provider) => providerLabelWithMode(provider, settingsDraft)),
|
||||
providerOrder: activeProviderOrder.map((provider) => buildProviderOrderEntry(provider, settingsDraft)),
|
||||
routing: routingEntries.map(([hosterId, provider]) => `${KNOWN_HOSTERS.find((hoster) => hoster.id === hosterId)?.label || hosterId} → ${providerLabelWithMode(provider, settingsDraft)}`),
|
||||
autoFallback: settingsDraft.autoProviderFallback,
|
||||
rememberCredentials: settingsDraft.rememberToken,
|
||||
|
||||
@@ -81,7 +81,7 @@ function getAccountPanelNavigationIndex(currentIndex: number, key: string): numb
|
||||
}
|
||||
|
||||
export interface AccountRulesViewModel {
|
||||
providerOrder: readonly string[];
|
||||
providerOrder: readonly { id: string; label: string; icon: string }[];
|
||||
routing: readonly string[];
|
||||
autoFallback: boolean;
|
||||
rememberCredentials?: boolean;
|
||||
@@ -508,17 +508,20 @@ function AccountRules({ model, actions }: AccountWorkspaceProps): ReactElement {
|
||||
{model.rules.providerOrder.map((provider, index) => (
|
||||
<li
|
||||
draggable={Boolean(actions.onProviderDragStart)}
|
||||
key={`${provider}-${index}`}
|
||||
key={provider.id}
|
||||
onDragEnd={actions.onProviderDragEnd}
|
||||
onDragOver={(event) => actions.onProviderDragOver?.(event, index)}
|
||||
onDragStart={(event) => actions.onProviderDragStart?.(event, index)}
|
||||
onDrop={(event) => actions.onProviderDrop?.(event, index)}
|
||||
>
|
||||
<span>{provider}</span>
|
||||
<span className="settings-provider-order-provider">
|
||||
<img alt="" aria-hidden="true" draggable={false} height="20" src={provider.icon} width="20" />
|
||||
<span>{provider.label}</span>
|
||||
</span>
|
||||
{actions.onMoveProvider ? (
|
||||
<span className="settings-provider-order-actions">
|
||||
<button aria-label={`${provider} nach oben`} disabled={index === 0} onClick={() => actions.onMoveProvider?.(index, -1)} type="button">↑</button>
|
||||
<button aria-label={`${provider} nach unten`} disabled={index === model.rules.providerOrder.length - 1} onClick={() => actions.onMoveProvider?.(index, 1)} type="button">↓</button>
|
||||
<button aria-label={`${provider.label} nach oben`} disabled={index === 0} onClick={() => actions.onMoveProvider?.(index, -1)} type="button">↑</button>
|
||||
<button aria-label={`${provider.label} nach unten`} disabled={index === model.rules.providerOrder.length - 1} onClick={() => actions.onMoveProvider?.(index, 1)} type="button">↓</button>
|
||||
</span>
|
||||
) : null}
|
||||
</li>
|
||||
|
||||
@@ -1067,6 +1067,19 @@
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.settings-provider-order-provider {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
gap: 9px;
|
||||
color: var(--ui-text);
|
||||
}
|
||||
|
||||
.settings-provider-order-provider img {
|
||||
flex: 0 0 auto;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.settings-provider-order-actions button {
|
||||
width: 30px;
|
||||
height: 28px;
|
||||
|
||||
@@ -4,7 +4,7 @@ import { renderToStaticMarkup } from "react-dom/server";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { defaultSettings } from "../src/main/constants";
|
||||
import { createRendererSettings, createRendererState } from "../src/main/renderer-state";
|
||||
import { buildAccountAddFields, buildAccountCreateProviderOrderUpdate, createAccountDialogState, createDiscardedSettingsState, createSettingsDraft, resolveSettingsSaveCompletion } from "../src/renderer/App";
|
||||
import { buildAccountAddFields, buildAccountCreateProviderOrderUpdate, buildProviderOrderEntry, createAccountDialogState, createDiscardedSettingsState, createSettingsDraft, resolveSettingsSaveCompletion } from "../src/renderer/App";
|
||||
import { buildAccountReplaceCommand, createAccountEditState, type AccountEditTarget } from "../src/renderer/account-edit";
|
||||
import {
|
||||
buildScopedAccountEnabledState,
|
||||
@@ -292,7 +292,10 @@ function workspaceModel(): AccountWorkspaceViewModel {
|
||||
selectedIds: [buildAccountRowId("megadebrid-api", "API", "mega-premium")],
|
||||
busy: false,
|
||||
rules: {
|
||||
providerOrder: ["Debrid-Link", "Real-Debrid"],
|
||||
providerOrder: [
|
||||
{ id: "debridlink", label: "Debrid-Link (API)", icon: "./provider-icons/debrid-link.ico" },
|
||||
{ id: "realdebrid", label: "Real-Debrid (Web-Login)", icon: "./provider-icons/real-debrid.png" }
|
||||
],
|
||||
routing: ["rapidgator.net → Debrid-Link"],
|
||||
autoFallback: true
|
||||
},
|
||||
@@ -1146,6 +1149,10 @@ describe("account workspace", () => {
|
||||
expect(html).toContain("Hoster-Routing");
|
||||
expect(html).toContain("Automatischer Fallback");
|
||||
expect(html).toContain("Provider-Laufzeit");
|
||||
expect(html).toContain("Debrid-Link (API)");
|
||||
expect(html).toContain("Real-Debrid (Web-Login)");
|
||||
expect(html).toContain("./provider-icons/debrid-link.ico");
|
||||
expect(html).toContain("./provider-icons/real-debrid.png");
|
||||
});
|
||||
|
||||
it("keeps add and edit dialogs separate and every secret field protected", () => {
|
||||
@@ -1347,6 +1354,27 @@ describe("account workspace", () => {
|
||||
});
|
||||
|
||||
describe("settings App integration", () => {
|
||||
it("projects provider order entries with logos and explicit login modes", () => {
|
||||
const settings = createRendererSettings({
|
||||
...defaultSettings(),
|
||||
megaDebridWebEnabled: true,
|
||||
megaDebridWebCredentials: "member:password",
|
||||
deepbridApiKey: "test-key",
|
||||
providerOrder: ["megadebrid-web", "deepbrid"]
|
||||
});
|
||||
|
||||
expect(buildProviderOrderEntry("megadebrid-web", settings)).toEqual({
|
||||
id: "megadebrid-web",
|
||||
label: "Mega-Debrid (Web-Login)",
|
||||
icon: "./provider-icons/mega-debrid.png"
|
||||
});
|
||||
expect(buildProviderOrderEntry("deepbrid", settings)).toEqual({
|
||||
id: "deepbrid",
|
||||
label: "Deepbrid (API)",
|
||||
icon: "./provider-icons/deepbrid.png"
|
||||
});
|
||||
});
|
||||
|
||||
it("preserves the write-only webhook during live snapshots and clears it for backup reseeding", () => {
|
||||
const safe = createRendererSettings({ ...defaultSettings(), notifyUrl: "https://private.example.test/hook" });
|
||||
const current = { ...safe, archivePasswordList: "loaded-password", notifyUrl: "https://private.example.test/hook" };
|
||||
|
||||
Reference in New Issue
Block a user