From 9578db10cae2d4a735838aef1f5c4641443bc064 Mon Sep 17 00:00:00 2001 From: YiJie Date: Thu, 10 Sep 2026 01:04:45 +0800 Subject: [PATCH 1/2] Use Host notifications for plugin operation feedback --- .agents/docs/notifications.md | 31 +++++ .gitignore | 2 + AGENTS.md | 16 +++ README.md | 4 + notification-sdk-evidence.json | 50 ++++++++ package-lock.json | 157 +++++------------------- package.json | 7 +- src/chatroom-composer.tsx | 15 +-- src/chatroom-entity-settings.tsx | 16 ++- src/chatroom-member-details.tsx | 11 +- src/chatroom-page-loader.tsx | 12 +- src/chatroom-page.tsx | 22 ++-- src/chatroom-room-actions.tsx | 13 +- src/chatroom-room-settings.tsx | 10 +- src/chatroom-timeline-entries.tsx | 13 +- src/chatroom-timeline.tsx | 24 ++-- src/chatroom.ts | 2 + src/entity-project-binding.tsx | 23 ++-- src/notifications.tsx | 9 ++ src/team-architecture-navigation.ts | 3 + src/team-architecture-page.tsx | 7 +- test/chatroom-composer.mjs | 11 +- test/chatroom-page-behavior.mjs | 10 +- test/entity-project-binding.mjs | 2 +- test/entity-templates.mjs | 4 +- test/helpers/component-hook-harness.mjs | 12 ++ test/real-cli-room.mjs | 16 ++- 27 files changed, 293 insertions(+), 209 deletions(-) create mode 100644 .agents/docs/notifications.md create mode 100644 notification-sdk-evidence.json create mode 100644 src/notifications.tsx diff --git a/.agents/docs/notifications.md b/.agents/docs/notifications.md new file mode 100644 index 0000000..3e41a3e --- /dev/null +++ b/.agents/docs/notifications.md @@ -0,0 +1,31 @@ +# Operation notifications + +Room actions, timeline copy/actions, send results, settings and project binding use the Host `ctx.notifications.show()` service. Source icon/name, +card interactions, queue, expiry and suppression rules belong to Host. +Declare `notifications` in injection requirements. Field validation and persistent +business state stay beside the relevant control or object. Do not introduce a +custom Toast, page-wide operation error, or raw exception message. + +The [Host guide](https://github.com/cordisx/cordisx/blob/3158c0401a3f604727f623f1a8ac584017dd8540/.agents/docs/notifications.md) +owns usage and interaction guidance; the +[Protocol contract](https://github.com/cordisx/cordisx-protocol/blob/dfa2c5fa956184df11a97955b8e5c74a76cb8876/.agents/docs/notifications-v1.md) +owns the public API. This migration is an experimental candidate, not a release. + +## Reproduce the candidate SDK + +The local SDK archive is deliberately excluded from Git. Its exact Host commit, +Protocol source and hashes are recorded in [notification-sdk-evidence.json](../../notification-sdk-evidence.json). +From a Host checkout at `efbff656d84b482d51598bc5ba303d24134e0c62`, run the +[SDK builder](https://github.com/cordisx/cordisx/blob/main/.agents/docs/sdk-source-packaging.md) +into a fresh directory, then run from this package: + +```sh +mkdir -p .cache/sdk +cp /path/to/sdk/packages/cordisx-0.1.0-beta.2.tgz .cache/sdk/cordisx-efbff656d84b.tgz +npm ci --ignore-scripts +npm run check +``` + +Compare the archive SHA-256 with the evidence before installation. Use one shared +Protocol override; independent copies break branded public types. This explicit +SDK build avoids recursive Git prepare scripts and does not change the API contract. diff --git a/.gitignore b/.gitignore index d2e8361..c8094fd 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ dist/ coverage/ .DS_Store cordisx.config.local.json + +.cache/sdk/ diff --git a/AGENTS.md b/AGENTS.md index 66e62dd..dd87906 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -13,3 +13,19 @@ Do not integrate through private Host/native DOM or a standalone replacement page. - Read `.agents/rules/README.md` before changing this repository. - Read the organization [CSS ownership and maintenance rule](https://github.com/cordisx/cordisxmono/blob/main/.agents/rules/css.md) before changing CSS, stylesheet-generating code, or a style-bearing DOM contract. + +## Operation notifications + +Use the public `ctx.notifications.show()` service for operation feedback and +require `notifications` in plugin injection. Do not create a custom Toast, +manually positioned alert, or page-wide success/error paragraph. Keep field +validation and durable business state beside the relevant object. Use stable +semantic `kind` values, localized safe text, and notification rules owned by Host; +never expose raw exceptions or notify on every polling attempt. +See the [Host notification guide](https://github.com/cordisx/cordisx/blob/3158c0401a3f604727f623f1a8ac584017dd8540/.agents/docs/notifications.md) +for the interaction contract and older-Host capability boundary. + +The notification migration currently uses exact feature-branch SDK/Protocol +revisions; it is not evidence of a formal release or a Mono pointer upgrade. + +Candidate setup: [notification migration](./.agents/docs/notifications.md). diff --git a/README.md b/README.md index 8696efc..a7b83af 100644 --- a/README.md +++ b/README.md @@ -218,3 +218,7 @@ validation; that does not make them a merged or released dependency. ## License [MIT](LICENSE) + +## Notification feedback + +See [operation notifications and candidate SDK setup](./.agents/docs/notifications.md). diff --git a/notification-sdk-evidence.json b/notification-sdk-evidence.json new file mode 100644 index 0000000..4bbcb09 --- /dev/null +++ b/notification-sdk-evidence.json @@ -0,0 +1,50 @@ +{ + "hostCommit": "efbff656d84b482d51598bc5ba303d24134e0c62", + "node": "v24.14.1", + "npm": "11.11.0", + "sources": [ + { + "location": "node_modules/@cordisx/channel", + "spec": "git+ssh://git@github.com/cordisx/plugin-channel.git#4cee12e3a92eeed557bc9de8cc4792710918327a" + }, + { + "location": "node_modules/@cordisx/eslint-config", + "spec": "git+ssh://git@github.com/cordisx/cordisxmono.git#c63c2e8c2ba7e11502934a52ad2ce3734e804cdc" + }, + { + "location": "node_modules/@cordisx/plugin-cli-proxy-api", + "spec": "git+ssh://git@github.com/cordisx/plugin-cli-proxy-api.git#1428ee205aab31df2779398cc8491879b303d1d0" + }, + { + "location": "node_modules/@cordisx/protocol", + "spec": "git+ssh://git@github.com/cordisx/cordisx-protocol.git#f3e18c925c34d90fdf203c158811cef10f7ebf57" + } + ], + "packages": [ + { + "filename": "cordisx-0.1.0-beta.2.tgz", + "sha256": "c3ea96b691a8e2e6b52ced3cf1a54fa29830e8b29538112312e036e5d3b5b42a", + "integrity": "sha512-f6wHDDz/2+71POc5WEaoiN7xsbHZJMpHob0pxnlH8YCpPAvU2uC8elNHOg7iasuwgry9QrAL5+hEOMRKc0nAgQ==" + }, + { + "filename": "cordisx-channel-0.1.0.tgz", + "sha256": "ca3fa6283bd5a6af41faf60d936373412246beba047dd652924f5b92060f09e5", + "integrity": "sha512-WqKvGxy72WwAUdwoLIHTBCG5/MAwVLKh2VmGK3tdSB+EMSUmuR+lAhxfdt9dYQRQL1Mh3t/w8SdYqps4mO0FkQ==" + }, + { + "filename": "cordisx-plugin-cli-proxy-api-0.1.0.tgz", + "sha256": "31d23ed0aa49372f2a8b77fcf74e8e80ba0cc8abf19aed8dc06305246f9d7830", + "integrity": "sha512-Otc61B3LTLweo0strMzH3XQH5I/P+HXAuFEFN+ynnnfvwscMj5BHovqAz2YS+JvPO79p+3WuCBTnEKh8DN/rkw==" + }, + { + "filename": "cordisx-protocol-0.1.0-alpha.0.tgz", + "sha256": "04e72b3c58ccb6722a6c56459da1e70c11d0162760eda29bb4846233ea8bb8fe", + "integrity": "sha512-o/YzvFs5gkrYsK0vijubLN/go2/oL04sixPz/Ks7N0eMLEHUL7qxD56Yf1ltW5dN9qfBIpgkHuiXgkSgOjj1bw==" + }, + { + "filename": "create-cordisx-plugin-0.1.0-beta.2.tgz", + "sha256": "3e4d16e315d4a911aabeff3cf6e3b21d64d3d4be7d9b9b07de610443027a5d30", + "integrity": "sha512-mdchmWwhZj/C938TpkiaNFjMhTFGzdYeG0bSk5AEhe+NdaoRmP3g0eUhu3DAfsvNNilp+vBbDmT4kWwcH9LwDg==" + } + ] +} diff --git a/package-lock.json b/package-lock.json index b3b5203..f769784 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ }, "devDependencies": { "@cordisx/eslint-config": "github:cordisx/cordisxmono#c63c2e8c2ba7e11502934a52ad2ce3734e804cdc", - "@cordisx/protocol": "github:cordisx/cordisx-protocol#a39c5d1f61999a69043180d6b7e7bbbee3072c09", + "@cordisx/protocol": "github:cordisx/cordisx-protocol#f3e18c925c34d90fdf203c158811cef10f7ebf57", "@deepseek-ai/cordis": "4.0.1", "@deepseek-ai/schemastery": "3.18.1", "@oneworks/avatar": "1.0.0-rc.9", @@ -25,7 +25,7 @@ "@vitejs/plugin-react": "6.1.0", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", - "cordisx": "github:cordisx/cordisx#e0a7d7b6fbd653dcf55b4105d3d193e49443826f", + "cordisx": "file:.cache/sdk/cordisx-efbff656d84b.tgz", "dprint": "0.57.1", "esbuild": "^0.28.2", "eslint": "9.39.4", @@ -143,16 +143,6 @@ "@keyv/serialize": "^1.1.1" } }, - "node_modules/@cordisx/channel": { - "version": "0.1.0", - "resolved": "git+ssh://git@github.com/cordisx/plugin-channel.git#4cee12e3a92eeed557bc9de8cc4792710918327a", - "integrity": "sha512-WqKvGxy72WwAUdwoLIHTBCG5/MAwVLKh2VmGK3tdSB+EMSUmuR+lAhxfdt9dYQRQL1Mh3t/w8SdYqps4mO0FkQ==", - "dev": true, - "license": "AGPL-3.0-or-later", - "engines": { - "node": ">=22.19" - } - }, "node_modules/@cordisx/eslint-config": { "version": "0.1.0", "resolved": "git+ssh://git@github.com/cordisx/cordisxmono.git#c63c2e8c2ba7e11502934a52ad2ce3734e804cdc", @@ -165,20 +155,10 @@ "eslint": "^9.39.4 || ^10.0.0" } }, - "node_modules/@cordisx/plugin-cli-proxy-api": { - "version": "0.1.0", - "resolved": "git+ssh://git@github.com/cordisx/plugin-cli-proxy-api.git#1428ee205aab31df2779398cc8491879b303d1d0", - "integrity": "sha512-EyIa/JSSHhbd9buCUd2gnI/Cdo47GYmJC+3FelfTNrA/krZSJN8TFE1c09yhySUtImtUliGv7ICa0XG+Ix/Kww==", - "dev": true, - "license": "AGPL-3.0-or-later", - "engines": { - "node": ">=22.19" - } - }, "node_modules/@cordisx/protocol": { "version": "0.1.0-alpha.0", - "resolved": "git+ssh://git@github.com/cordisx/cordisx-protocol.git#a39c5d1f61999a69043180d6b7e7bbbee3072c09", - "integrity": "sha512-42+ErrJ8cJvXj0LB9Qt3+kAycATyXT5ifsM3HfjtwYDtSBTcSVZmhF/Tod5fFzut2jM7IvWvyr5SF1jxXKpnLQ==", + "resolved": "git+ssh://git@github.com/cordisx/cordisx-protocol.git#f3e18c925c34d90fdf203c158811cef10f7ebf57", + "integrity": "sha512-o/YzvFs5gkrYsK0vijubLN/go2/oL04sixPz/Ks7N0eMLEHUL7qxD56Yf1ltW5dN9qfBIpgkHuiXgkSgOjj1bw==", "dev": true, "license": "AGPL-3.0-or-later", "engines": { @@ -2796,22 +2776,20 @@ } }, "node_modules/cordisx": { - "name": "cordisx-monorepo", "version": "0.1.0-beta.2", - "resolved": "git+ssh://git@github.com/cordisx/cordisx.git#e0a7d7b6fbd653dcf55b4105d3d193e49443826f", - "integrity": "sha512-YTPu42VaCvABOAmgstRlBBmsn8YzNDw5KRa4PtfVXjWWu3mr3qUxVVEoBDTFr+qgh+PrHz74SsHSlCj7Hy5pnQ==", + "resolved": "file:.cache/sdk/cordisx-efbff656d84b.tgz", + "integrity": "sha512-f6wHDDz/2+71POc5WEaoiN7xsbHZJMpHob0pxnlH8YCpPAvU2uC8elNHOg7iasuwgry9QrAL5+hEOMRKc0nAgQ==", "bundleDependencies": [ - "@cordisx/schemastery-ui" + "@cordisx/schemastery-ui", + "@cordisx/channel", + "@cordisx/plugin-cli-proxy-api" ], "dev": true, "license": "AGPL-3.0-or-later", - "workspaces": [ - "packages/*" - ], "dependencies": { - "@cordisx/channel": "github:cordisx/plugin-channel#4cee12e3a92eeed557bc9de8cc4792710918327a", - "@cordisx/plugin-cli-proxy-api": "github:cordisx/plugin-cli-proxy-api#1428ee205aab31df2779398cc8491879b303d1d0", - "@cordisx/protocol": "github:cordisx/cordisx-protocol#a39c5d1f61999a69043180d6b7e7bbbee3072c09", + "@cordisx/channel": "0.1.0", + "@cordisx/plugin-cli-proxy-api": "0.1.0", + "@cordisx/protocol": "github:cordisx/cordisx-protocol#f3e18c925c34d90fdf203c158811cef10f7ebf57", "@cordisx/schemastery-ui": "0.1.0-beta.2", "@deepseek-ai/cordis": "4.0.1", "@deepseek-ai/schemastery": "^3.18.1", @@ -2821,6 +2799,7 @@ "@shikitor/core": "1.0.2", "@types/react": "19.2.18", "@types/react-dom": "19.2.5", + "@vitejs/plugin-react": "6.1.0", "debug": "4.4.3", "es-module-lexer": "1.7.0", "esbuild": "0.28.2", @@ -2843,15 +2822,34 @@ "swr": "2.5.1", "tar": "7.5.22", "tdesign-react": "1.18.2", + "vite": "8.2.2", "ws": "^8.18.3" }, "bin": { - "cordisx": "packages/cli/dist/src/cli.js" + "cordisx": "dist/src/cli.js" }, "engines": { "node": ">=22.19" } }, + "node_modules/cordisx/node_modules/@cordisx/channel": { + "version": "0.1.0", + "dev": true, + "inBundle": true, + "license": "AGPL-3.0-or-later", + "engines": { + "node": ">=22.19" + } + }, + "node_modules/cordisx/node_modules/@cordisx/plugin-cli-proxy-api": { + "version": "0.1.0", + "dev": true, + "inBundle": true, + "license": "AGPL-3.0-or-later", + "engines": { + "node": ">=22.19" + } + }, "node_modules/cordisx/node_modules/@cordisx/schemastery-ui": { "version": "0.1.0-beta.2", "dev": true, @@ -2878,74 +2876,6 @@ "react-dom": ">=18.3.0 <20" } }, - "node_modules/cordisx/packages/cli": { - "name": "cordisx", - "version": "0.1.0-beta.2", - "bundleDependencies": [ - "@cordisx/schemastery-ui" - ], - "extraneous": true, - "license": "AGPL-3.0-or-later", - "dependencies": { - "@cordisx/channel": "github:cordisx/plugin-channel#4cee12e3a92eeed557bc9de8cc4792710918327a", - "@cordisx/plugin-cli-proxy-api": "github:cordisx/plugin-cli-proxy-api#1428ee205aab31df2779398cc8491879b303d1d0", - "@cordisx/protocol": "github:cordisx/cordisx-protocol#a39c5d1f61999a69043180d6b7e7bbbee3072c09", - "@cordisx/schemastery-ui": "0.1.0-beta.2", - "@deepseek-ai/cordis": "4.0.1", - "@deepseek-ai/schemastery": "^3.18.1", - "@larksuiteoapi/node-sdk": "^1.73.0", - "@oneworks/avatar": "1.0.0-rc.8", - "@oneworks/avatar-react": "1.0.0-rc.8", - "@shikitor/core": "1.0.2", - "@types/react": "19.2.18", - "@types/react-dom": "19.2.5", - "@vitejs/plugin-react": "6.1.0", - "debug": "4.4.3", - "es-module-lexer": "1.7.0", - "esbuild": "0.28.2", - "extend": "3.0.2", - "intl-messageformat": "11.2.14", - "lightningcss": "1.33.0", - "luna-console": "1.3.6", - "luna-data-grid": "1.6.5", - "luna-dom-viewer": "1.8.4", - "luna-object-viewer": "0.3.2", - "react": "19.2.8", - "react-dom": "19.2.8", - "react-markdown": "10.1.0", - "rehype-raw": "^7.0.0", - "rehype-sanitize": "^6.0.0", - "reicon": "1.2.1", - "remark-gfm": "4.0.1", - "shiki": "4.4.3", - "style-to-js": "1.1.21", - "swr": "2.5.1", - "tar": "7.5.22", - "tdesign-react": "1.18.2", - "vite": "8.2.2", - "ws": "^8.18.3" - }, - "bin": { - "cordisx": "dist/src/cli.js" - }, - "devDependencies": { - "@cordisx/channel-runtime": "file:../channel-runtime", - "@cordisx/protocol": "github:cordisx/cordisx-protocol#a39c5d1f61999a69043180d6b7e7bbbee3072c09", - "@types/node": "^22.18.1", - "@types/react": "19.2.18", - "@types/react-dom": "19.2.5", - "@types/ws": "^8.18.1", - "ajv": "8.20.0", - "ajv-formats": "3.0.1", - "jsdom": "^26.1.0", - "tsx": "^4.20.5", - "typescript": "^5.9.2", - "vitest": "^3.2.4" - }, - "engines": { - "node": ">=22.19" - } - }, "node_modules/cosmiconfig": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.2.tgz", @@ -7783,27 +7713,6 @@ "dev": true, "license": "0BSD" }, - "node_modules/tsx": { - "version": "4.23.13", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.13.tgz", - "integrity": "sha512-BL5MGkRln6aDYhb0xbQlEAGw743BaZYWdbWtdJOBriYJboKgUUYCadFp2/FpBBZquBC/ezNBn7wMMPx7FDZUDw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "esbuild": "~0.28.0" - }, - "bin": { - "tsx": "dist/cli.mjs" - }, - "engines": { - "node": ">=18.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - } - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/package.json b/package.json index 3ef0fd3..a7b338a 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ }, "devDependencies": { "@cordisx/eslint-config": "github:cordisx/cordisxmono#c63c2e8c2ba7e11502934a52ad2ce3734e804cdc", - "@cordisx/protocol": "github:cordisx/cordisx-protocol#a39c5d1f61999a69043180d6b7e7bbbee3072c09", + "@cordisx/protocol": "github:cordisx/cordisx-protocol#f3e18c925c34d90fdf203c158811cef10f7ebf57", "@deepseek-ai/cordis": "4.0.1", "@deepseek-ai/schemastery": "3.18.1", "@oneworks/avatar": "1.0.0-rc.9", @@ -47,7 +47,7 @@ "@vitejs/plugin-react": "6.1.0", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", - "cordisx": "github:cordisx/cordisx#e0a7d7b6fbd653dcf55b4105d3d193e49443826f", + "cordisx": "file:.cache/sdk/cordisx-efbff656d84b.tgz", "dprint": "0.57.1", "esbuild": "^0.28.2", "eslint": "9.39.4", @@ -64,5 +64,8 @@ }, "bin": { "cordisx-chatroom": "./dist/agent-tools/cli/chatroom.mjs" + }, + "overrides": { + "@cordisx/protocol": "$@cordisx/protocol" } } diff --git a/src/chatroom-composer.tsx b/src/chatroom-composer.tsx index 6cee7c2..c1e32d8 100644 --- a/src/chatroom-composer.tsx +++ b/src/chatroom-composer.tsx @@ -1,3 +1,4 @@ +import { useNotifications } from './notifications.js'; import { type KeyboardEvent, useEffect, useId, useLayoutEffect, useRef, useState } from 'cordisx/react'; import type { CordisXReactPageProps } from 'cordisx/contracts'; import { @@ -117,7 +118,7 @@ export function ChatroomComposer( const [draft, setDraft] = useState(''); const [sending, setSending] = useState(false); const [aborted, setAborted] = useState(signal.aborted); - const [notice, setNotice] = useState(); + const notifications = useNotifications(); const [error, setError] = useState(); const [query, setQuery] = useState(); const [activeIndex, setActiveIndex] = useState(0); @@ -231,7 +232,6 @@ export function ChatroomComposer( setSending(true); setQuery(undefined); setError(undefined); - setNotice(undefined); try { if (firstMessage !== undefined) { const result = await firstMessage(submittedDraft); @@ -252,14 +252,16 @@ export function ChatroomComposer( if (!mounted.current || signal.aborted) return; const result = source.pageComposerCompletion(completion); if (result.status !== 'accepted') { - setError(t('composer.send-failed')); + notifications.show({ kind: 'message.send-failed', type: 'error', message: t('composer.send-failed') }); return; } } setDraft(current => current === submittedDraft ? '' : current); - setNotice(t('composer.sent')); + notifications.show({ kind: 'message.sent', type: 'success', message: t('composer.sent') }); } catch { - if (mounted.current && !signal.aborted) setError(t('composer.send-failed')); + if (mounted.current && !signal.aborted) { + notifications.show({ kind: 'message.send-failed', type: 'error', message: t('composer.send-failed') }); + } } finally { busy.current = false; if (mounted.current && !signal.aborted) setSending(false); @@ -355,7 +357,6 @@ export function ChatroomComposer( onValueChange={value => { draftRef.current = value; setDraft(value); - setNotice(undefined); setError(undefined); updateQuery(value, input.current?.getSelection()); }} @@ -414,7 +415,7 @@ export function ChatroomComposer( {t(shortcutPolicy === 'enter' ? 'composer.shortcut.enter' : 'composer.shortcut.mod-enter')}

- {unavailable ? t('composer.unavailable') : sending ? t('composer.sending') : notice} + {unavailable ? t('composer.unavailable') : sending ? t('composer.sending') : undefined}
{error !== undefined &&

{error}

} diff --git a/src/chatroom-entity-settings.tsx b/src/chatroom-entity-settings.tsx index 53f2599..7916c17 100644 --- a/src/chatroom-entity-settings.tsx +++ b/src/chatroom-entity-settings.tsx @@ -1,3 +1,4 @@ +import { useNotifications } from './notifications.js'; import { useEffect, useRef, useState } from 'cordisx/react'; import { Button, Icon } from 'cordisx/ui'; import type { CordisXReactPageProps } from 'cordisx/contracts'; @@ -12,13 +13,12 @@ export function ChatroomEntitySettings({ room, participantId, details, t }: { }) { const [available, setAvailable] = useState(false); const [busy, setBusy] = useState(false); - const [failed, setFailed] = useState(false); + const notifications = useNotifications(); const pending = useRef(false); const member = room.memberships.find(candidate => candidate.participantId === participantId); useEffect(() => { let cancelled = false; setAvailable(false); - setFailed(false); void details.entitySettingsAvailable(room, participantId).then(value => { if (!cancelled) setAvailable(value); }).catch(() => {}); @@ -37,11 +37,16 @@ export function ChatroomEntitySettings({ room, participantId, details, t }: { if (pending.current) return; pending.current = true; setBusy(true); - setFailed(false); try { - if (!await details.openEntitySettings(room, participantId)) setFailed(true); + if (!await details.openEntitySettings(room, participantId)) { + notifications.show({ + kind: 'settings.open-failed', + type: 'error', + message: t('identity.settings.failed'), + }); + } } catch { - setFailed(true); + notifications.show({ kind: 'settings.open-failed', type: 'error', message: t('identity.settings.failed') }); } finally { pending.current = false; setBusy(false); @@ -50,7 +55,6 @@ export function ChatroomEntitySettings({ room, participantId, details, t }: { >