Keep configured IM channels editable

This commit is contained in:
taohe
2026-06-11 14:37:58 +08:00
parent c966eb71a7
commit 9d51e38641
9 changed files with 110 additions and 68 deletions
@@ -60,6 +60,8 @@ export function ChannelRuntimeConfigDialog({
return null;
}
const isEditing = provider.configured;
const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
onSubmit(provider, values);
@@ -71,7 +73,9 @@ export function ChannelRuntimeConfigDialog({
<form onSubmit={handleSubmit} className="space-y-4">
<DialogHeader>
<DialogTitle>
{t.channels.setupTitle(provider.display_name)}
{isEditing
? t.channels.setupEditTitle(provider.display_name)
: t.channels.setupTitle(provider.display_name)}
</DialogTitle>
<DialogDescription>{t.channels.setupDescription}</DialogDescription>
</DialogHeader>
@@ -118,7 +122,7 @@ export function ChannelRuntimeConfigDialog({
{submitting ? (
<LoaderCircleIcon className="animate-spin" />
) : null}
{t.channels.saveAndConnect}
{isEditing ? t.channels.saveChanges : t.channels.saveAndConnect}
</Button>
</DialogFooter>
</form>
@@ -37,6 +37,10 @@ function providerCanConnect(provider: ChannelProvider): boolean {
);
}
function providerCanEditRuntimeConfig(provider: ChannelProvider): boolean {
return provider.enabled && (provider.credential_fields?.length ?? 0) > 0;
}
function getProviderUnavailableReason(
provider: ChannelProvider,
t: ReturnType<typeof useI18n>["t"],
@@ -126,6 +130,7 @@ export function WorkspaceChannelsList() {
<SidebarGroupLabel>{t.sidebar.channels}</SidebarGroupLabel>
<SidebarMenu>
{visibleProviders.map((provider) => {
const canEditRuntimeConfig = providerCanEditRuntimeConfig(provider);
const isConnected = provider.connection_status === "connected";
const isPending =
(connectMutation.isPending &&
@@ -153,10 +158,13 @@ export function WorkspaceChannelsList() {
"h-8 w-24 px-2 text-xs",
isConnected && "gap-1",
)}
disabled={isConnected || isPending}
disabled={isPending}
title={unavailableReason}
onClick={() => {
if (providerNeedsRuntimeConfig(provider)) {
if (
providerNeedsRuntimeConfig(provider) ||
canEditRuntimeConfig
) {
setSetupProvider(provider);
return;
}
@@ -193,16 +201,13 @@ export function WorkspaceChannelsList() {
}
}}
onSubmit={(provider, values) => {
const connectWindow =
provider.auth_mode === "deep_link" ? prepareConnectWindow() : null;
void configureMutation
.mutateAsync({ provider: provider.provider, values })
.then((configuredProvider) => {
.then(() => {
setSetupProvider(null);
startConnect(configuredProvider, connectWindow);
toast.success(t.channels.connected);
})
.catch((error) => {
closeConnectWindow(connectWindow);
toast.error(
error instanceof Error ? error.message : t.channels.unavailable,
);
@@ -108,6 +108,10 @@ function providerNeedsRuntimeConfig(provider: ChannelProvider): boolean {
);
}
function providerCanEditRuntimeConfig(provider: ChannelProvider): boolean {
return provider.enabled && (provider.credential_fields?.length ?? 0) > 0;
}
function ChannelProviderItem({
provider,
connection,
@@ -120,7 +124,10 @@ function ChannelProviderItem({
const configureMutation = useConfigureChannelProvider();
const disconnectMutation = useDisconnectChannelConnection();
const [setupOpen, setSetupOpen] = useState(false);
const isConnected = connection?.status === "connected";
const isConnected =
connection?.status === "connected" ||
provider.connection_status === "connected";
const canEditRuntimeConfig = providerCanEditRuntimeConfig(provider);
const canConnect =
(provider.connectable ?? (provider.enabled && provider.configured)) &&
!isConnected;
@@ -195,21 +202,41 @@ function ChannelProviderItem({
</ItemDescription>
</ItemContent>
<ItemActions className="ml-auto">
{isConnected && connection ? (
<Button
type="button"
variant="outline"
size="sm"
disabled={isDisconnecting}
onClick={() => disconnectMutation.mutate(connection.id)}
>
{isDisconnecting ? (
<LoaderCircleIcon className="animate-spin" />
) : (
<UnplugIcon />
)}
{t.channels.disconnect}
</Button>
{isConnected ? (
<>
{canEditRuntimeConfig ? (
<Button
type="button"
variant="outline"
size="sm"
disabled={isConnecting}
onClick={() => setSetupOpen(true)}
>
{isConnecting ? (
<LoaderCircleIcon className="animate-spin" />
) : (
<PlugIcon />
)}
{t.channels.modify}
</Button>
) : null}
{connection ? (
<Button
type="button"
variant="outline"
size="sm"
disabled={isDisconnecting}
onClick={() => disconnectMutation.mutate(connection.id)}
>
{isDisconnecting ? (
<LoaderCircleIcon className="animate-spin" />
) : (
<UnplugIcon />
)}
{t.channels.disconnect}
</Button>
) : null}
</>
) : (
<Button
type="button"
@@ -217,7 +244,10 @@ function ChannelProviderItem({
disabled={isConnecting}
title={unavailableReason}
onClick={() => {
if (providerNeedsRuntimeConfig(provider)) {
if (
providerNeedsRuntimeConfig(provider) ||
canEditRuntimeConfig
) {
setSetupOpen(true);
return;
}
@@ -248,18 +278,13 @@ function ChannelProviderItem({
submitting={configureMutation.isPending}
onOpenChange={setSetupOpen}
onSubmit={(submitProvider, values) => {
const connectWindow =
submitProvider.auth_mode === "deep_link"
? prepareConnectWindow()
: null;
void configureMutation
.mutateAsync({ provider: submitProvider.provider, values })
.then((configuredProvider) => {
.then(() => {
setSetupOpen(false);
startConnect(configuredProvider, connectWindow);
toast.success(t.channels.connected);
})
.catch((error) => {
closeConnectWindow(connectWindow);
toast.error(
error instanceof Error ? error.message : t.channels.unavailable,
);