Align IM connections with local channels

This commit is contained in:
taohe
2026-06-10 22:16:47 +08:00
parent 92c185b90d
commit d06643d8a2
33 changed files with 588 additions and 1536 deletions
@@ -106,12 +106,20 @@ export function WorkspaceChannelsList() {
disabled={!canConnect || isPending}
title={getProviderDisabledReason(provider, t)}
onClick={() => {
const connectWindow = prepareConnectWindow();
const connectWindow =
provider.auth_mode === "deep_link"
? prepareConnectWindow()
: null;
void connectMutation
.mutateAsync(provider.provider)
.then((result) =>
openConnectUrl(result.url, connectWindow),
)
.then((result) => {
if (result.url) {
openConnectUrl(result.url, connectWindow);
return;
}
closeConnectWindow(connectWindow);
toast.success(result.instruction);
})
.catch((error) => {
closeConnectWindow(connectWindow);
toast.error(
@@ -165,10 +165,20 @@ function ChannelProviderItem({
disabled={!canConnect || isConnecting}
title={unavailableReason}
onClick={() => {
const connectWindow = prepareConnectWindow();
const connectWindow =
provider.auth_mode === "deep_link"
? prepareConnectWindow()
: null;
void connectMutation
.mutateAsync(provider.provider)
.then((result) => openConnectUrl(result.url, connectWindow))
.then((result) => {
if (result.url) {
openConnectUrl(result.url, connectWindow);
return;
}
closeConnectWindow(connectWindow);
toast.success(result.instruction);
})
.catch((error) => {
closeConnectWindow(connectWindow);
toast.error(
+3 -1
View File
@@ -35,6 +35,8 @@ export interface ChannelConnectionsResponse {
export interface ChannelConnectResponse {
provider: ChannelProviderId;
mode: string;
url: string;
url?: string | null;
code: string;
instruction: string;
expires_in: number;
}
+9 -8
View File
@@ -23,7 +23,7 @@ function mockChannelsAPI(page: Page) {
display_name: "Slack",
enabled: true,
configured: true,
auth_mode: "oauth",
auth_mode: "binding_code",
connection_status: "not_connected",
},
{
@@ -31,7 +31,7 @@ function mockChannelsAPI(page: Page) {
display_name: "Discord",
enabled: true,
configured: true,
auth_mode: "oauth_and_bot_install",
auth_mode: "binding_code",
connection_status: "not_connected",
},
],
@@ -53,8 +53,10 @@ function mockChannelsAPI(page: Page) {
contentType: "application/json",
body: JSON.stringify({
provider: "slack",
mode: "oauth",
url: "http://localhost:3000/mock-slack-oauth?client_id=dev&state=test",
mode: "binding_code",
url: null,
code: "abc123",
instruction: "Send /connect abc123 to the DeerFlow Slack bot.",
expires_in: 600,
}),
});
@@ -91,11 +93,10 @@ test.describe("IM channels", () => {
const connectButtons = dialog.getByRole("button", { name: "Connect" });
await expect(connectButtons).toHaveCount(3);
const popupPromise = page.waitForEvent("popup");
await connectButtons.nth(1).click();
const popup = await popupPromise;
await expect(page).toHaveURL(/\/workspace\/chats\/new/);
await expect(popup).toHaveURL(/\/mock-slack-oauth/);
await popup.close();
await expect(
page.getByText("Send /connect abc123 to the DeerFlow Slack bot."),
).toBeVisible();
});
});
@@ -85,6 +85,8 @@ describe("channels api", () => {
provider: "telegram",
mode: "deep_link",
url: "https://t.me/deerflow_bot?start=state",
code: "state",
instruction: "Send /start state to the DeerFlow Telegram bot.",
expires_in: 600,
}),
);
@@ -92,6 +94,7 @@ describe("channels api", () => {
await expect(connectChannelProvider("telegram")).resolves.toMatchObject({
provider: "telegram",
url: "https://t.me/deerflow_bot?start=state",
instruction: "Send /start state to the DeerFlow Telegram bot.",
});
expect(mockedFetch).toHaveBeenCalledWith(
"/backend/api/channels/telegram/connect",
@@ -99,6 +102,26 @@ describe("channels api", () => {
);
});
test("starts a binding-code connection flow", async () => {
mockedFetch.mockResolvedValueOnce(
jsonResponse(200, {
provider: "slack",
mode: "binding_code",
url: null,
code: "abc123",
instruction: "Send /connect abc123 to the DeerFlow Slack bot.",
expires_in: 600,
}),
);
await expect(connectChannelProvider("slack")).resolves.toMatchObject({
provider: "slack",
url: null,
code: "abc123",
instruction: "Send /connect abc123 to the DeerFlow Slack bot.",
});
});
test("disconnects a channel connection", async () => {
mockedFetch.mockResolvedValueOnce(new Response(null, { status: 204 }));
@@ -69,9 +69,11 @@ describe("channel connect window helpers", () => {
test("falls back to current-window navigation when no popup is available", () => {
const { assign } = stubWindow(null);
openConnectUrl("https://slack.com/oauth/v2/authorize");
openConnectUrl("https://t.me/deerflow_bot?start=state");
expect(assign).toHaveBeenCalledWith("https://slack.com/oauth/v2/authorize");
expect(assign).toHaveBeenCalledWith(
"https://t.me/deerflow_bot?start=state",
);
});
test("closes a prepared popup on connect failure", () => {