feat(FN-3403): add shell multi-profile support with connection manager onbo
Merges shell multi-profile support for desktop (FN-3403) — spanning new connection-manager flows, shell onboarding interoperability, mobile connection profiles, desktop IPC/shell-settings plumbing, and tokenized titlebar styles — with a secondary migration of the WhatsApp plugin to the Baileys pairi Fusion-Task-Id: FN-3403
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import {
|
||||
createOrUpdateProfile,
|
||||
DEFAULT_WEB_SHELL_STATE,
|
||||
deleteProfile,
|
||||
normalizeShellState,
|
||||
selectActiveProfile,
|
||||
} from "../shell-connection-settings";
|
||||
|
||||
describe("shell-connection-settings", () => {
|
||||
it("normalizes missing active profile to null", () => {
|
||||
const state = normalizeShellState({
|
||||
host: "mobile-shell",
|
||||
activeProfileId: "missing",
|
||||
profiles: [{ id: "p1", name: "Prod", serverUrl: "https://fusion.example.com", createdAt: "", updatedAt: "" }],
|
||||
});
|
||||
|
||||
expect(state.activeProfileId).toBeNull();
|
||||
expect(state.profiles).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("returns web fallback for empty shell state", () => {
|
||||
expect(normalizeShellState(undefined)).toEqual(DEFAULT_WEB_SHELL_STATE);
|
||||
});
|
||||
|
||||
it("throws unsupported errors in browser mode", async () => {
|
||||
await expect(createOrUpdateProfile(null, { name: "Prod", serverUrl: "https://fusion.example.com" })).rejects.toThrow(
|
||||
"Saving connection profiles is only available in native shell mode",
|
||||
);
|
||||
await expect(deleteProfile(null, "p1")).rejects.toThrow("Deleting connection profiles is only available in native shell mode");
|
||||
await expect(selectActiveProfile(null, "p1")).rejects.toThrow(
|
||||
"Switching connection profiles is only available in native shell mode",
|
||||
);
|
||||
});
|
||||
|
||||
it("delegates profile actions to shell API", async () => {
|
||||
const shellApi = {
|
||||
saveProfile: vi.fn(async () => ({ id: "p1", name: "Prod", serverUrl: "https://fusion.example.com", createdAt: "", updatedAt: "" })),
|
||||
deleteProfile: vi.fn(async () => undefined),
|
||||
setActiveProfile: vi.fn(async () => ({ host: "mobile-shell", activeProfileId: "p1", profiles: [] })),
|
||||
} as const;
|
||||
|
||||
await createOrUpdateProfile(shellApi as never, { name: "Prod", serverUrl: "https://fusion.example.com" });
|
||||
await deleteProfile(shellApi as never, "p1");
|
||||
await selectActiveProfile(shellApi as never, "p1");
|
||||
|
||||
expect(shellApi.saveProfile).toHaveBeenCalled();
|
||||
expect(shellApi.deleteProfile).toHaveBeenCalledWith("p1");
|
||||
expect(shellApi.setActiveProfile).toHaveBeenCalledWith("p1");
|
||||
});
|
||||
});
|
||||
64
packages/dashboard/app/utils/shell-connection-settings.ts
Normal file
64
packages/dashboard/app/utils/shell-connection-settings.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import type {
|
||||
FusionShellApi,
|
||||
ShellConnectionProfile,
|
||||
ShellConnectionProfileInput,
|
||||
ShellConnectionState,
|
||||
} from "../types/native-shell";
|
||||
|
||||
export const DEFAULT_WEB_SHELL_STATE: ShellConnectionState = {
|
||||
host: "web",
|
||||
activeProfileId: null,
|
||||
profiles: [],
|
||||
};
|
||||
|
||||
export function normalizeShellState(state: ShellConnectionState | null | undefined): ShellConnectionState {
|
||||
if (!state) {
|
||||
return DEFAULT_WEB_SHELL_STATE;
|
||||
}
|
||||
|
||||
const profiles = Array.isArray(state.profiles)
|
||||
? state.profiles.filter((profile): profile is ShellConnectionProfile => Boolean(profile && profile.id && profile.serverUrl))
|
||||
: [];
|
||||
|
||||
const activeProfileId =
|
||||
state.activeProfileId && profiles.some((profile) => profile.id === state.activeProfileId)
|
||||
? state.activeProfileId
|
||||
: null;
|
||||
|
||||
return {
|
||||
...state,
|
||||
activeProfileId,
|
||||
profiles,
|
||||
};
|
||||
}
|
||||
|
||||
function unsupportedError(action: string): Error {
|
||||
return new Error(`${action} is only available in native shell mode`);
|
||||
}
|
||||
|
||||
export async function createOrUpdateProfile(
|
||||
shellApi: FusionShellApi | null,
|
||||
profile: ShellConnectionProfileInput,
|
||||
): Promise<ShellConnectionProfile> {
|
||||
if (!shellApi) {
|
||||
throw unsupportedError("Saving connection profiles");
|
||||
}
|
||||
return shellApi.saveProfile(profile);
|
||||
}
|
||||
|
||||
export async function deleteProfile(shellApi: FusionShellApi | null, profileId: string): Promise<void> {
|
||||
if (!shellApi) {
|
||||
throw unsupportedError("Deleting connection profiles");
|
||||
}
|
||||
await shellApi.deleteProfile(profileId);
|
||||
}
|
||||
|
||||
export async function selectActiveProfile(
|
||||
shellApi: FusionShellApi | null,
|
||||
profileId: string | null,
|
||||
): Promise<ShellConnectionState> {
|
||||
if (!shellApi) {
|
||||
throw unsupportedError("Switching connection profiles");
|
||||
}
|
||||
return shellApi.setActiveProfile(profileId);
|
||||
}
|
||||
Reference in New Issue
Block a user