From 5d8a2bf2844f2c38aa4e0b308f7c480db34ce5f4 Mon Sep 17 00:00:00 2001 From: hiroshisuga <45039819+hiroshisuga@users.noreply.github.com> Date: Sun, 20 Sep 2026 16:43:24 +0900 Subject: [PATCH 01/27] Update SDK version and plugin version in manifest --- manifest.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/manifest.json b/manifest.json index 55d6251..7ea6213 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { - "requiredSdkVersion": "~0.0.59", + "requiredSdkVersion": "~0.0.59 || ^0.1.2", "name": "BbbPluginSessionShare", - "version": "0.0.1", + "version": "0.0.2", "javascriptEntrypointUrl": "BbbPluginSessionShare.js", - "localesBaseUrl": "https://cdn.domain.com/pluginabc/" + "localesBaseUrl": "locales" } From a954358f8be8e59152bc60634fcd475e5f31bab0 Mon Sep 17 00:00:00 2001 From: hiroshisuga <45039819+hiroshisuga@users.noreply.github.com> Date: Sun, 20 Sep 2026 16:46:10 +0900 Subject: [PATCH 02/27] Update bigbluebutton-html-plugin-sdk version --- package.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 676f65f..bc51e3b 100644 --- a/package.json +++ b/package.json @@ -10,11 +10,11 @@ "@types/react": "^18.2.13", "@types/react-dom": "^18.2.6", "babel-plugin-syntax-dynamic-import": "^6.18.0", - "bigbluebutton-html-plugin-sdk": "0.0.93", + "bigbluebutton-html-plugin-sdk": "0.1.26", "path": "^0.12.7", "react": "^18.2.0", - "react-copy-to-clipboard": "^5.1.0", "react-dom": "^18.2.0", + "react-intl": "^6.8.9" "react-modal": "^3.16.1", "react-qr-code": "^2.0.12", "react-select": "^5.7.3" @@ -45,7 +45,6 @@ "@babel/preset-react": "^7.18.6", "@types/node": "^20.4.4", "@types/react": "^18.2.15", - "@types/react-copy-to-clipboard": "^5.0.7", "@types/react-dom": "^18.2.7", "@types/react-modal": "^3.16.0", "@types/styled-components": "^5.1.26", From 038619a5fd7e4f69f10200d7093818a862693aaf Mon Sep 17 00:00:00 2001 From: hiroshisuga <45039819+hiroshisuga@users.noreply.github.com> Date: Sun, 20 Sep 2026 16:55:27 +0900 Subject: [PATCH 03/27] Update lockfile for SDK and UI dependency changes --- package-lock.json | 183 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 141 insertions(+), 42 deletions(-) diff --git a/package-lock.json b/package-lock.json index aeff58c..2b273a1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,11 +14,11 @@ "@types/react": "^18.2.13", "@types/react-dom": "^18.2.6", "babel-plugin-syntax-dynamic-import": "^6.18.0", - "bigbluebutton-html-plugin-sdk": "0.0.93", + "bigbluebutton-html-plugin-sdk": "0.1.26", "path": "^0.12.7", "react": "^18.2.0", - "react-copy-to-clipboard": "^5.1.0", "react-dom": "^18.2.0", + "react-intl": "^6.8.9", "react-modal": "^3.16.1", "react-qr-code": "^2.0.12", "react-select": "^5.7.3" @@ -29,7 +29,6 @@ "@babel/preset-react": "^7.18.6", "@types/node": "^20.4.4", "@types/react": "^18.2.15", - "@types/react-copy-to-clipboard": "^5.0.7", "@types/react-dom": "^18.2.7", "@types/react-modal": "^3.16.0", "@types/styled-components": "^5.1.26", @@ -2088,6 +2087,101 @@ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.4.tgz", "integrity": "sha512-qprfWkn82Iw821mcKofJ5Pk9wgioHicxcQMxx+5zt5GSKoqdWvgG5AxVmpmUUjzTLPVSH5auBrhI93Deayn/DA==" }, + "node_modules/@formatjs/ecma402-abstract": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.2.4.tgz", + "integrity": "sha512-lFyiQDVvSbQOpU+WFd//ILolGj4UgA/qXrKeZxdV14uKiAUiPAtX6XAn7WBCRi7Mx6I7EybM9E5yYn4BIpZWYg==", + "license": "MIT", + "dependencies": { + "@formatjs/fast-memoize": "2.2.3", + "@formatjs/intl-localematcher": "0.5.8", + "tslib": "2" + } + }, + "node_modules/@formatjs/fast-memoize": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.3.tgz", + "integrity": "sha512-3jeJ+HyOfu8osl3GNSL4vVHUuWFXR03Iz9jjgI7RwjG6ysu/Ymdr0JRCPHfF5yGbTE6JCrd63EpvX1/WybYRbA==", + "license": "MIT", + "dependencies": { + "tslib": "2" + } + }, + "node_modules/@formatjs/icu-messageformat-parser": { + "version": "2.9.4", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.9.4.tgz", + "integrity": "sha512-Tbvp5a9IWuxUcpWNIW6GlMQYEc4rwNHR259uUFoKWNN1jM9obf9Ul0e+7r7MvFOBNcN+13K7NuKCKqQiAn1QEg==", + "license": "MIT", + "dependencies": { + "@formatjs/ecma402-abstract": "2.2.4", + "@formatjs/icu-skeleton-parser": "1.8.8", + "tslib": "2" + } + }, + "node_modules/@formatjs/icu-skeleton-parser": { + "version": "1.8.8", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.8.tgz", + "integrity": "sha512-vHwK3piXwamFcx5YQdCdJxUQ1WdTl6ANclt5xba5zLGDv5Bsur7qz8AD7BevaKxITwpgDeU0u8My3AIibW9ywA==", + "license": "MIT", + "dependencies": { + "@formatjs/ecma402-abstract": "2.2.4", + "tslib": "2" + } + }, + "node_modules/@formatjs/intl": { + "version": "2.10.15", + "resolved": "https://registry.npmjs.org/@formatjs/intl/-/intl-2.10.15.tgz", + "integrity": "sha512-i6+xVqT+6KCz7nBfk4ybMXmbKO36tKvbMKtgFz9KV+8idYFyFbfwKooYk8kGjyA5+T5f1kEPQM5IDLXucTAQ9g==", + "license": "MIT", + "dependencies": { + "@formatjs/ecma402-abstract": "2.2.4", + "@formatjs/fast-memoize": "2.2.3", + "@formatjs/icu-messageformat-parser": "2.9.4", + "@formatjs/intl-displaynames": "6.8.5", + "@formatjs/intl-listformat": "7.7.5", + "intl-messageformat": "10.7.7", + "tslib": "2" + }, + "peerDependencies": { + "typescript": "^4.7 || 5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@formatjs/intl-displaynames": { + "version": "6.8.5", + "resolved": "https://registry.npmjs.org/@formatjs/intl-displaynames/-/intl-displaynames-6.8.5.tgz", + "integrity": "sha512-85b+GdAKCsleS6cqVxf/Aw/uBd+20EM0wDpgaxzHo3RIR3bxF4xCJqH/Grbzx8CXurTgDDZHPdPdwJC+May41w==", + "license": "MIT", + "dependencies": { + "@formatjs/ecma402-abstract": "2.2.4", + "@formatjs/intl-localematcher": "0.5.8", + "tslib": "2" + } + }, + "node_modules/@formatjs/intl-listformat": { + "version": "7.7.5", + "resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-7.7.5.tgz", + "integrity": "sha512-Wzes10SMNeYgnxYiKsda4rnHP3Q3II4XT2tZyOgnH5fWuHDtIkceuWlRQNsvrI3uiwP4hLqp2XdQTCsfkhXulg==", + "license": "MIT", + "dependencies": { + "@formatjs/ecma402-abstract": "2.2.4", + "@formatjs/intl-localematcher": "0.5.8", + "tslib": "2" + } + }, + "node_modules/@formatjs/intl-localematcher": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.8.tgz", + "integrity": "sha512-I+WDNWWJFZie+jkfkiK5Mp4hEDyRSEvmyfYadflOno/mmKJKcB17fEpEH0oJu/OWhhCJ8kJBDz2YMd/6cDl7Mg==", + "license": "MIT", + "dependencies": { + "tslib": "2" + } + }, "node_modules/@graphql-typed-document-node/core": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", @@ -2540,7 +2634,6 @@ "version": "3.3.5", "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz", "integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==", - "dev": true, "dependencies": { "@types/react": "*", "hoist-non-react-statics": "^3.3.0" @@ -2617,15 +2710,6 @@ "csstype": "^3.0.2" } }, - "node_modules/@types/react-copy-to-clipboard": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/@types/react-copy-to-clipboard/-/react-copy-to-clipboard-5.0.7.tgz", - "integrity": "sha512-Gft19D+as4M+9Whq1oglhmK49vqPhcLzk8WfvfLvaYMIPYanyfLy0+CwFucMJfdKoSFyySPmkkWn8/E6voQXjQ==", - "dev": true, - "dependencies": { - "@types/react": "*" - } - }, "node_modules/@types/react-dom": { "version": "18.2.7", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.7.tgz", @@ -3720,9 +3804,10 @@ "dev": true }, "node_modules/bigbluebutton-html-plugin-sdk": { - "version": "0.0.93", - "resolved": "https://registry.npmjs.org/bigbluebutton-html-plugin-sdk/-/bigbluebutton-html-plugin-sdk-0.0.93.tgz", - "integrity": "sha512-oA6qdOmNm93tYGo0xedHF+tAeTrq3rgd7es7WP/VBWP/vMfLNOCjhLQ4BM55tckA1xhAGuEnwR0+SXBE7ot9hg==", + "version": "0.1.26", + "resolved": "https://registry.npmjs.org/bigbluebutton-html-plugin-sdk/-/bigbluebutton-html-plugin-sdk-0.1.26.tgz", + "integrity": "sha512-EANOpZiIPsm69npnQf20r6ymhjyQAr8p3zjHyyPHwm7avFtDUt/5kkVBzDBq7emHr4WSYCDrVOxphqCywxC+eg==", + "license": "LGPL-3.0", "dependencies": { "@apollo/client": "^3.8.7", "@browser-bunyan/console-formatted-stream": "^1.8.0", @@ -4152,14 +4237,6 @@ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", "dev": true }, - "node_modules/copy-to-clipboard": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", - "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==", - "dependencies": { - "toggle-selection": "^1.0.6" - } - }, "node_modules/copy-webpack-plugin": { "version": "12.0.2", "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-12.0.2.tgz", @@ -6329,6 +6406,18 @@ "node": ">=10.13.0" } }, + "node_modules/intl-messageformat": { + "version": "10.7.7", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.7.tgz", + "integrity": "sha512-F134jIoeYMro/3I0h08D0Yt4N9o9pjddU/4IIxMMURqbAtI2wu70X8hvG1V48W49zXHXv3RKSF/po+0fDfsGjA==", + "license": "BSD-3-Clause", + "dependencies": { + "@formatjs/ecma402-abstract": "2.2.4", + "@formatjs/fast-memoize": "2.2.3", + "@formatjs/icu-messageformat-parser": "2.9.4", + "tslib": "2" + } + }, "node_modules/ipaddr.js": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.1.0.tgz", @@ -8266,18 +8355,6 @@ "node": ">=0.10.0" } }, - "node_modules/react-copy-to-clipboard": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.1.0.tgz", - "integrity": "sha512-k61RsNgAayIJNoy9yDsYzDe/yAZAzEbEgcz3DZMhF686LEyukcE1hzurxe85JandPUG+yTfGVFzuEw3xt8WP/A==", - "dependencies": { - "copy-to-clipboard": "^3.3.1", - "prop-types": "^15.8.1" - }, - "peerDependencies": { - "react": "^15.3.0 || 16 || 17 || 18" - } - }, "node_modules/react-dom": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", @@ -8290,6 +8367,33 @@ "react": "^18.2.0" } }, + "node_modules/react-intl": { + "version": "6.8.9", + "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-6.8.9.tgz", + "integrity": "sha512-TUfj5E7lyUDvz/GtovC9OMh441kBr08rtIbgh3p0R8iF3hVY+V2W9Am7rb8BpJ/29BH1utJOqOOhmvEVh3GfZg==", + "license": "BSD-3-Clause", + "dependencies": { + "@formatjs/ecma402-abstract": "2.2.4", + "@formatjs/icu-messageformat-parser": "2.9.4", + "@formatjs/intl": "2.10.15", + "@formatjs/intl-displaynames": "6.8.5", + "@formatjs/intl-listformat": "7.7.5", + "@types/hoist-non-react-statics": "3", + "@types/react": "16 || 17 || 18", + "hoist-non-react-statics": "3", + "intl-messageformat": "10.7.7", + "tslib": "2" + }, + "peerDependencies": { + "react": "^16.6.0 || 17 || 18", + "typescript": "^4.7 || 5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", @@ -9537,11 +9641,6 @@ "node": ">=8.0" } }, - "node_modules/toggle-selection": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", - "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==" - }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -9831,7 +9930,7 @@ "version": "5.2.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", - "dev": true, + "devOptional": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" From a52f1aa82e20f952fa1ed6cabc4ce7b2b80d9403 Mon Sep 17 00:00:00 2001 From: hiroshisuga <45039819+hiroshisuga@users.noreply.github.com> Date: Sun, 20 Sep 2026 17:01:50 +0900 Subject: [PATCH 04/27] Update style.css --- src/config-modal/style.css | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/config-modal/style.css b/src/config-modal/style.css index 89da65d..08b7238 100644 --- a/src/config-modal/style.css +++ b/src/config-modal/style.css @@ -168,4 +168,15 @@ cursor: pointer; margin-bottom: 15px; } + + .share-url-field { + margin-inline: auto; + box-sizing: border-box; + } + + .error-message { + margin-top: 15px; + color: #b00020; + text-align: center; + } } From 8216f8b066cdb49b89651ce523b25d53c5d2a7b6 Mon Sep 17 00:00:00 2001 From: hiroshisuga <45039819+hiroshisuga@users.noreply.github.com> Date: Sun, 20 Sep 2026 17:02:52 +0900 Subject: [PATCH 05/27] Add internationalized UI messages --- src/i18n.ts | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 src/i18n.ts diff --git a/src/i18n.ts b/src/i18n.ts new file mode 100644 index 0000000..4c16d83 --- /dev/null +++ b/src/i18n.ts @@ -0,0 +1,96 @@ +import { defineMessages } from 'react-intl'; + +const intlMessages = defineMessages({ + toolbarInvite: { + id: 'plugin.sessionShare.toolbar.invite', + defaultMessage: 'Invite', + description: 'Tooltip for the session-share action button', + }, + shareOptions: { + id: 'plugin.sessionShare.title.shareOptions', + defaultMessage: 'Share Options', + description: 'Title of the share-type selection screen', + }, + joinMeeting: { + id: 'plugin.sessionShare.title.joinMeeting', + defaultMessage: 'Join This Meeting', + description: 'Title shown above an invitation QR code', + }, + shareCurrentSession: { + id: 'plugin.sessionShare.title.shareCurrentSession', + defaultMessage: 'Share Current Session', + description: 'Title shown above a current-session QR code', + }, + inviteOtherUsers: { + id: 'plugin.sessionShare.option.inviteOtherUsers', + defaultMessage: 'Invite other users', + description: 'Option for inviting users through the frontend login URL', + }, + shareMySession: { + id: 'plugin.sessionShare.option.shareMySession', + defaultMessage: 'Share my session', + description: 'Option for sharing the current user session', + }, + confirm: { + id: 'plugin.sessionShare.button.confirm', + defaultMessage: 'Confirm', + description: 'Confirmation button', + }, + close: { + id: 'plugin.sessionShare.button.close', + defaultMessage: 'Close', + description: 'Close button', + }, + copyUrl: { + id: 'plugin.sessionShare.button.copyUrl', + defaultMessage: 'Copy URL', + description: 'Button for copying the displayed URL', + }, + fullScreen: { + id: 'plugin.sessionShare.button.fullScreen', + defaultMessage: 'Full screen', + description: 'Button for entering full-screen mode', + }, + copied: { + id: 'plugin.sessionShare.status.copied', + defaultMessage: 'Copied to clipboard.', + description: 'Message shown after copying the URL', + }, + shareUrl: { + id: 'plugin.sessionShare.field.shareUrl', + defaultMessage: 'Share URL', + description: 'Accessible label for the URL field', + }, + popupBlocked: { + id: 'plugin.sessionShare.error.popupBlocked', + defaultMessage: 'The share window was blocked by the browser. Please allow pop-ups for this site.', + description: 'Error shown when the browser blocks the share window', + }, + meetingLoading: { + id: 'plugin.sessionShare.error.meetingLoading', + defaultMessage: 'Meeting information is still loading. Please try again.', + description: 'Error shown while meeting information is loading', + }, + invitationUrlUnavailable: { + id: 'plugin.sessionShare.error.invitationUrlUnavailable', + defaultMessage: 'An invitation URL was not provided for this meeting.', + description: 'Error shown when an invitation URL was not provided', + }, + shareUrlFailed: { + id: 'plugin.sessionShare.error.shareUrlFailed', + defaultMessage: 'Failed to create the share URL.', + description: 'Generic URL creation error', + }, + fullScreenFailed: { + id: 'plugin.sessionShare.error.fullScreenFailed', + defaultMessage: 'The browser could not enter full-screen mode.', + description: 'Error shown when full-screen mode fails', + }, + copyFailed: { + id: 'plugin.sessionShare.error.copyFailed', + defaultMessage: 'Could not copy the URL. The URL has been selected; copy it manually.', + description: 'Error shown when copying the URL fails', + }, +}); + +export default intlMessages; From 8d10ecc190a583732ce374faf2ac5673c46049aa Mon Sep 17 00:00:00 2001 From: hiroshisuga <45039819+hiroshisuga@users.noreply.github.com> Date: Sun, 20 Sep 2026 17:03:23 +0900 Subject: [PATCH 06/27] Replace the in-client modal with a movable popup --- src/config-modal/modal.tsx | 493 +++++++++++++++++++++++++++++-------- 1 file changed, 397 insertions(+), 96 deletions(-) diff --git a/src/config-modal/modal.tsx b/src/config-modal/modal.tsx index bf7928b..42d1940 100644 --- a/src/config-modal/modal.tsx +++ b/src/config-modal/modal.tsx @@ -1,112 +1,413 @@ -// modal.tsx import * as React from 'react'; import { useState } from 'react'; -import * as ReactModal from 'react-modal'; -import './style.css'; +import { createPortal } from 'react-dom'; import QRCode from 'react-qr-code'; -import { CopyToClipboard } from 'react-copy-to-clipboard'; +import { IntlShape } from 'react-intl'; +import intlMessages from '../i18n'; -interface ShareModalProps { - isOpen: boolean; +interface ShareWindowProps { + intl: IntlShape; + popupWindow: Window | null; newJoinUrl: string; onClose: () => void; - onConfirm: (shareType: string) => void; + onConfirm: (shareType: string) => Promise; } -export function ShareModal({ - isOpen, newJoinUrl, onClose, onConfirm, -}: ShareModalProps) { - const [shareType, setShareType] = useState('shareSession'); +const POPUP_STYLES = ` + :root { + color-scheme: light; + font-family: Arial, Helvetica, sans-serif; + } + + * { + box-sizing: border-box; + } + + body { + margin: 0; + min-width: 360px; + min-height: 100vh; + color: #172b4d; + background: #f4f6f9; + } + + button, + input { + font: inherit; + } + + .session-share-window { + width: min(100%, 600px); + min-height: 100vh; + margin: 0 auto; + padding: 18px; + display: flex; + flex-direction: column; + align-items: center; + background: #ffffff; + } + + .session-share-window h1 { + margin: 0 0 16px; + font-size: 22px; + } + + .share-options { + width: 100%; + display: flex; + flex-direction: column; + gap: 8px; + } + + .share-options label { + display: flex; + align-items: center; + gap: 10px; + padding: 10px 12px; + border: 1px solid #d8dde6; + border-radius: 6px; + cursor: pointer; + } + + .share-options input { + margin: 0; + } + + .share-url-field { + display: block; + width: 100%; + max-width: 560px; + margin: 0 auto 14px; + padding: 10px; + border: 1px solid #8993a4; + border-radius: 4px; + } + + .qr-wrapper { + width: 100%; + display: flex; + justify-content: center; + padding: 18px; + background: #ffffff; + } + + .qr-code { + width: min(320px, 80vw); + height: auto; + } + + .buttons-container { + width: 100%; + margin-top: 16px; + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 10px; + } + + .buttons-container button { + padding: 10px 18px; + border: 0; + border-radius: 5px; + color: #ffffff; + background: #0f70d7; + cursor: pointer; + } + + .buttons-container button.secondary { + background: #022852; + } + + .buttons-container button:disabled { + cursor: wait; + opacity: 0.6; + } + + .error-message { + margin-top: 16px; + color: #b00020; + text-align: center; + } + + .copied-message { + min-height: 22px; + margin: 8px 0 0; + color: #176b3a; + } + + :fullscreen .session-share-window { + width: 100%; + max-width: none; + justify-content: center; + } + + :fullscreen .qr-code { + width: min(65vmin, 640px); + } +`; + +export function ShareWindow({ + intl, popupWindow, newJoinUrl, onClose, onConfirm, +}: ShareWindowProps) { + const [shareType, setShareType] = useState('inviteUsers'); const [loading, setLoading] = useState(false); + const [errorMessage, setErrorMessage] = useState(''); + const [copied, setCopied] = useState(false); + const shareUrlInputRef = React.useRef(null); + + let screenTitle = intl.formatMessage(intlMessages.shareOptions); + if (newJoinUrl && shareType === 'inviteUsers') { + screenTitle = intl.formatMessage(intlMessages.joinMeeting); + } else if (newJoinUrl) { + screenTitle = intl.formatMessage( + intlMessages.shareCurrentSession, + ); + } + + // Inject CSS directly to the popup + React.useEffect(() => { + if (!popupWindow) return undefined; + + const style = popupWindow.document.createElement('style'); + style.dataset.sessionShare = 'true'; + style.textContent = POPUP_STYLES; + popupWindow.document.head.appendChild(style); - // This is required for accessibility reasons. - // You can alternatively set it on your root app component during app initialization. - ReactModal.setAppElement('#app'); // Adjust '#root' to match your application's mount node ID. + setShareType('inviteUsers'); + setLoading(false); + setErrorMessage(''); + setCopied(false); + + return () => style.remove(); + }, [popupWindow]); React.useEffect(() => { - if (isOpen) { + if (!popupWindow || popupWindow.closed) return; + + if (newJoinUrl) { + // For showing URL or QR code + popupWindow.resizeTo(520, 700); + } else { + // For showing Invite/Share option + popupWindow.resizeTo(460, 360); + } + }, [popupWindow, newJoinUrl]); + + const handleConfirm = async () => { + setLoading(true); + setErrorMessage(''); + + try { + await onConfirm(shareType); + } catch (error) { + setErrorMessage( + error instanceof Error + ? error.message + : intl.formatMessage(intlMessages.shareUrlFailed), + ); + } finally { setLoading(false); } - }, [isOpen]); - - return isOpen && ( - document.querySelector('#modals-container')} - overlayClassName="modal-overlay" - > -
-

Share Options

- { - /** Join URL not created */ - !newJoinUrl - ? ( - <> -
- - -
-
- - -
- - ) : null - } - { - /** Join URL available */ - newJoinUrl - ? ( - <> - -
- - {' '} - Copy -
-
- - -
- -
- - ) : null - } -
-
+ }; + + const selectShareUrl = () => { + const input = shareUrlInputRef.current; + + if (!input) return false; + + input.focus(); + input.select(); + input.setSelectionRange(0, input.value.length); + + return true; + }; + + const handleCopyUrl = async () => { + if (!popupWindow || !newJoinUrl) return; + + setCopied(false); + setErrorMessage(''); + + try { + const clipboard = popupWindow.navigator.clipboard; + + if (clipboard && popupWindow.isSecureContext) { + await clipboard.writeText(newJoinUrl); + setCopied(true); + return; + } + + // fallback for browsers without clipboard API + const selected = selectShareUrl(); + const copiedByCommand = selected + && popupWindow.document.execCommand('copy'); + + if (!copiedByCommand) { + throw new Error('Copy command failed'); + } + + setCopied(true); + } catch (error) { + // Try old copy command when Clipboard API is denied + try { + const selected = selectShareUrl(); + const copiedByCommand = selected + && popupWindow.document.execCommand('copy'); + + if (copiedByCommand) { + setCopied(true); + return; + } + } catch (fallbackError) { + } + + // Enable manual copying even when every copying method failed + selectShareUrl(); + + setErrorMessage( + intl.formatMessage(intlMessages.copyFailed), + ); + } + }; + + const handleFullscreen = async () => { + if (!popupWindow) return; + + try { + if (popupWindow.document.fullscreenElement) { + await popupWindow.document.exitFullscreen(); + } else { + await popupWindow.document.documentElement.requestFullscreen(); + } + } catch (error) { + setErrorMessage( + intl.formatMessage(intlMessages.fullScreenFailed), + ); + } + }; + + if (!popupWindow) return null; + + const popupRoot = popupWindow.document.getElementById( + 'session-share-root', + ); + + if (!popupRoot) return null; + + return createPortal( +
+

{screenTitle}

+ + {!newJoinUrl ? ( + <> +
+ + + +
+ + {errorMessage ? ( +
+ {errorMessage} +
+ ) : null} + +
+ + + +
+ + ) : ( + <> + event.currentTarget.select()} + /> + +
+ +
+ +
+ {copied ? intl.formatMessage(intlMessages.copied) : ''} +
+ + {errorMessage ? ( +
+ {errorMessage} +
+ ) : null} + +
+ + + + + +
+ + )} +
, + popupRoot, ); } From de5195e022051896a05f2a788b287e750cf1fd8e Mon Sep 17 00:00:00 2001 From: hiroshisuga <45039819+hiroshisuga@users.noreply.github.com> Date: Sun, 20 Sep 2026 17:04:03 +0900 Subject: [PATCH 07/27] Use the meeting login URL and add entry points for BBB 3.0 and 4.0 --- src/main/component.tsx | 217 +++++++++++++++++++++++++++++++++++------ 1 file changed, 189 insertions(+), 28 deletions(-) diff --git a/src/main/component.tsx b/src/main/component.tsx index 12f5efd..4e6fa08 100644 --- a/src/main/component.tsx +++ b/src/main/component.tsx @@ -1,59 +1,220 @@ import * as React from 'react'; -import { useEffect } from 'react'; +import { useEffect, useRef } from 'react'; import { ActionsBarButton, ActionsBarInterface, ActionsBarPosition, - ActionsBarSeparator, BbbPluginSdk, PluginApi, + ActionsBarSeparator, AppsGalleryEntry, BbbPluginSdk, PluginApi, } from 'bigbluebutton-html-plugin-sdk'; import { SessionSharePluginProps } from './types'; -import { ShareModal } from '../config-modal/modal'; +import { ShareWindow } from '../config-modal/modal'; + +import { createIntl, createIntlCache } from 'react-intl'; +import intlMessages from '../i18n'; + +const intlCache = createIntlCache(); function SessionSharePlugin({ pluginUuid: uuid, }: SessionSharePluginProps): React.ReactNode { BbbPluginSdk.initialize(uuid); const pluginApi: PluginApi = BbbPluginSdk.getPluginApi(uuid); - const [isModalOpen, setModalOpen] = React.useState(false); + const meetingInfoGraphqlResponse = pluginApi.useMeeting(); + const inviteUrl = meetingInfoGraphqlResponse?.data?.loginUrl?.trim() ?? ''; + const { + messages: localeMessages, + currentLocale, + } = pluginApi.useLocaleMessages!(); + + const intl = React.useMemo(() => createIntl({ + locale: currentLocale || 'en', + messages: localeMessages || {}, + fallbackOnEmptyString: true, + }, intlCache), [currentLocale, localeMessages]); + + const popupWindowRef = useRef(null); + const [popupWindow, setPopupWindow] = React.useState(null); const [newJoinUrl, setNewJoinUrl] = React.useState(''); - // Toolbar button + const openShareWindow = () => { + const currentPopup = popupWindowRef.current; + + // Forward the popup if already shown + if (currentPopup && !currentPopup.closed) { + currentPopup.focus(); + setPopupWindow(currentPopup); + return; + } + + const popupWidth = 460; + const popupHeight = 360; + + // show the popup relative to the BBB window + const popupLeft = Math.round( + window.screenX + + Math.max(0, (window.outerWidth - popupWidth) / 2), + ); + + const popupTop = Math.round( + window.screenY + + Math.max(0, (window.outerHeight - popupHeight) / 2), + ); + + const popup = window.open( + '', + 'bbb-session-share', + [ + 'popup=yes', + `width=${popupWidth}`, + `height=${popupHeight}`, + `left=${popupLeft}`, + `top=${popupTop}`, + 'resizable=yes', + 'scrollbars=yes', + ].join(','), + ); + + if (!popup) { + window.alert( + intl.formatMessage(intlMessages.popupBlocked), + ); + return; + } + + popup.document.title = intl.formatMessage(intlMessages.toolbarInvite); + popup.document.documentElement.lang = intl.locale; + popup.document.body.replaceChildren(); + + const viewport = popup.document.createElement('meta'); + viewport.name = 'viewport'; + viewport.content = 'width=device-width, initial-scale=1'; + popup.document.head.appendChild(viewport); + + const popupRoot = popup.document.createElement('div'); + popupRoot.id = 'session-share-root'; + popup.document.body.appendChild(popupRoot); + + const handlePopupClosed = () => { + if (popupWindowRef.current === popup) { + popupWindowRef.current = null; + setPopupWindow(null); + setNewJoinUrl(''); + } + }; + + popup.addEventListener( + 'beforeunload', + handlePopupClosed, + { once: true }, + ); + + popupWindowRef.current = popup; + setNewJoinUrl(''); + setPopupWindow(popup); + popup.focus(); + }; + + const closeShareWindow = () => { + const currentPopup = popupWindowRef.current; + + popupWindowRef.current = null; + setPopupWindow(null); + setNewJoinUrl(''); + + if (currentPopup && !currentPopup.closed) { + currentPopup.close(); + } + }; + + // Registration to Toolbar / Apps Gellery useEffect(() => { - const buttonToUserListItem: - ActionsBarInterface = new ActionsBarButton({ - icon: { - iconName: 'add', - }, - tooltip: 'Invite', - onClick: () => { - setModalOpen(true); - }, - position: ActionsBarPosition.RIGHT, - }); - const dropdownToUserListItem: - ActionsBarInterface = new ActionsBarSeparator({ - position: ActionsBarPosition.RIGHT, + const label = intl.formatMessage(intlMessages.toolbarInvite); + + const supportsAppsGallery = typeof pluginApi.setAppsGalleryItems === 'function'; + + if (supportsAppsGallery) { + // BBB 4.0 -> Apps Gallery + const appsGalleryEntry = new AppsGalleryEntry({ + id: 'bbb-plugin-session-share', + name: label, + icon: { + iconName: 'add', + }, + dataTest: 'sessionShareAppsGalleryEntry', + onClick: openShareWindow, }); - pluginApi.setActionsBarItems([dropdownToUserListItem, buttonToUserListItem]); + pluginApi.setAppsGalleryItems([ + appsGalleryEntry, + ]); + } else { + // BBB 3.0 -> Action bar + const buttonToUserListItem: ActionsBarInterface = + new ActionsBarButton({ + icon: { + iconName: 'add', + }, + tooltip: label, + onClick: openShareWindow, + position: ActionsBarPosition.RIGHT, + }); + + const dropdownToUserListItem: ActionsBarInterface = + new ActionsBarSeparator({ + position: ActionsBarPosition.RIGHT, + }); + + pluginApi.setActionsBarItems([ + dropdownToUserListItem, + buttonToUserListItem, + ]); + } + + return () => { + if (supportsAppsGallery) { + pluginApi.setAppsGalleryItems([]); + } else { + pluginApi.setActionsBarItems([]); + } + }; + }, [pluginApi, intl]); + + // Close popup when BBB is closed. + useEffect(() => () => { + const currentPopup = popupWindowRef.current; + popupWindowRef.current = null; + + if (currentPopup && !currentPopup.closed) { + currentPopup.close(); + } }, []); - // Handle popup confirmation const handleConfirm = async (shareType: string) => { - let joinOptions = {}; - if (shareType === 'inviteUsers') { - joinOptions = { fullName: null }; + if (meetingInfoGraphqlResponse?.loading) { + throw new Error( + intl.formatMessage(intlMessages.meetingLoading), + ); + } + if (!inviteUrl) { + throw new Error( + intl.formatMessage(intlMessages.invitationUrlUnavailable), + ); + } + + setNewJoinUrl(inviteUrl); + return; } - const joinUrl = await pluginApi.getJoinUrl(joinOptions); + const joinUrl = await pluginApi.getJoinUrl({}); setNewJoinUrl(joinUrl); }; return ( - { setModalOpen(false); setNewJoinUrl(null); }} + onClose={closeShareWindow} onConfirm={handleConfirm} /> ); From 57236e33c797c91368ba0e2af4c582fa15a5ee5a Mon Sep 17 00:00:00 2001 From: hiroshisuga <45039819+hiroshisuga@users.noreply.github.com> Date: Sun, 20 Sep 2026 17:05:28 +0900 Subject: [PATCH 08/27] Add locales directory to CopyWebpackPlugin patterns --- webpack.config.js | 1 + 1 file changed, 1 insertion(+) diff --git a/webpack.config.js b/webpack.config.js index dc1fa71..c12c39b 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -58,6 +58,7 @@ module.exports = { new CopyWebpackPlugin({ patterns: [ { from: 'manifest.json', to: './' }, // Copy manifest.json to static/ in the output folder + { from: 'public/locales/', to: './locales/' }, ], }), ], From 7e3862e48b5b5f28786ae838d4040639b41a4379 Mon Sep 17 00:00:00 2001 From: hiroshisuga <45039819+hiroshisuga@users.noreply.github.com> Date: Sun, 20 Sep 2026 18:48:07 +0900 Subject: [PATCH 09/27] Update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bc51e3b..ee4a4ae 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "path": "^0.12.7", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-intl": "^6.8.9" + "react-intl": "^6.8.9", "react-modal": "^3.16.1", "react-qr-code": "^2.0.12", "react-select": "^5.7.3" From 84b267b27de47b027c2e7db67cd8aa97a65dfe5a Mon Sep 17 00:00:00 2001 From: hiroshisuga <45039819+hiroshisuga@users.noreply.github.com> Date: Sun, 20 Sep 2026 18:54:45 +0900 Subject: [PATCH 10/27] Add English and Japanese locale files --- public/locales/en.json | 20 ++++++++++++++++++++ public/locales/index.json | 5 +++++ public/locales/ja.json | 20 ++++++++++++++++++++ 3 files changed, 45 insertions(+) create mode 100644 public/locales/en.json create mode 100644 public/locales/index.json create mode 100644 public/locales/ja.json diff --git a/public/locales/en.json b/public/locales/en.json new file mode 100644 index 0000000..ad053a3 --- /dev/null +++ b/public/locales/en.json @@ -0,0 +1,20 @@ +{ + "plugin.sessionShare.toolbar.invite": "Share Session", + "plugin.sessionShare.title.shareOptions": "Choose How to Share", + "plugin.sessionShare.title.joinMeeting": "Join Meeting Using This QR Code", + "plugin.sessionShare.title.shareCurrentSession": "Open This Session on Another Device", + "plugin.sessionShare.option.inviteOtherUsers": "Invite other users", + "plugin.sessionShare.option.shareMySession": "Open my session on another device", + "plugin.sessionShare.button.confirm": "Confirm", + "plugin.sessionShare.button.close": "Close", + "plugin.sessionShare.button.copyUrl": "Copy URL", + "plugin.sessionShare.button.fullScreen": "Full screen", + "plugin.sessionShare.status.copied": "Copied to clipboard.", + "plugin.sessionShare.field.shareUrl": "Share URL", + "plugin.sessionShare.error.popupBlocked": "The share window was blocked by the browser. Please allow pop-ups for this site.", + "plugin.sessionShare.error.meetingLoading": "Meeting information is still loading. Please try again.", + "plugin.sessionShare.error.invitationUrlUnavailable": "An invitation URL was not provided for this meeting.", + "plugin.sessionShare.error.shareUrlFailed": "Failed to create the share URL.", + "plugin.sessionShare.error.fullScreenFailed": "The browser could not enter full-screen mode.", + "plugin.sessionShare.error.copyFailed": "Could not copy the URL. The URL has been selected; copy it manually." +} diff --git a/public/locales/index.json b/public/locales/index.json new file mode 100644 index 0000000..84967b4 --- /dev/null +++ b/public/locales/index.json @@ -0,0 +1,5 @@ +[ + "en.json", + "ja.json", + "index.json" +] diff --git a/public/locales/ja.json b/public/locales/ja.json new file mode 100644 index 0000000..6de2d03 --- /dev/null +++ b/public/locales/ja.json @@ -0,0 +1,20 @@ +{ + "plugin.sessionShare.toolbar.invite": "会議の共有", + "plugin.sessionShare.title.shareOptions": "共有方法を選択", + "plugin.sessionShare.title.joinMeeting": "QRコードから参加してください", + "plugin.sessionShare.title.shareCurrentSession": "別のデバイスでこのセッションを開いてください", + "plugin.sessionShare.option.inviteOtherUsers": "他のユーザーを招待", + "plugin.sessionShare.option.shareMySession": "このセッションを別のデバイスで開く", + "plugin.sessionShare.button.confirm": "表示", + "plugin.sessionShare.button.close": "閉じる", + "plugin.sessionShare.button.copyUrl": "URLをコピー", + "plugin.sessionShare.button.fullScreen": "全画面表示", + "plugin.sessionShare.status.copied": "クリップボードにコピーしました。", + "plugin.sessionShare.field.shareUrl": "共有URL", + "plugin.sessionShare.error.popupBlocked": "共有ウインドウがブラウザによってブロックされました。このサイトのポップアップを許可してください。", + "plugin.sessionShare.error.meetingLoading": "会議情報を読み込んでいます。しばらくしてからもう一度お試しください。", + "plugin.sessionShare.error.invitationUrlUnavailable": "この会議には招待URLが設定されていません。", + "plugin.sessionShare.error.shareUrlFailed": "共有URLを作成できませんでした。", + "plugin.sessionShare.error.fullScreenFailed": "全画面表示に切り替えられませんでした。", + "plugin.sessionShare.error.copyFailed": "URLをコピーできませんでした。URLは選択状態になっているので、手動でコピーしてください。" +} From 626768c9d3d41e558b3a1ba475742115c5cb706c Mon Sep 17 00:00:00 2001 From: hiroshisuga <45039819+hiroshisuga@users.noreply.github.com> Date: Sun, 20 Sep 2026 19:01:29 +0900 Subject: [PATCH 11/27] Document manual-copy fallback in copy handling Add comment for manual-copy fallback in modal.tsx --- src/config-modal/modal.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/config-modal/modal.tsx b/src/config-modal/modal.tsx index 42d1940..070bc57 100644 --- a/src/config-modal/modal.tsx +++ b/src/config-modal/modal.tsx @@ -259,6 +259,7 @@ export function ShareWindow({ return; } } catch (fallbackError) { + // Continue to the manual-copy fallback below. } // Enable manual copying even when every copying method failed From 25b0add52e3ca15c8278b3e4935d16d0d415e46e Mon Sep 17 00:00:00 2001 From: hiroshisuga <45039819+hiroshisuga@users.noreply.github.com> Date: Sun, 20 Sep 2026 19:03:28 +0900 Subject: [PATCH 12/27] Fix lint and formatting in popup --- src/config-modal/modal.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/config-modal/modal.tsx b/src/config-modal/modal.tsx index 070bc57..ab57526 100644 --- a/src/config-modal/modal.tsx +++ b/src/config-modal/modal.tsx @@ -229,7 +229,7 @@ export function ShareWindow({ setErrorMessage(''); try { - const clipboard = popupWindow.navigator.clipboard; + const { clipboard } = popupWindow.navigator; if (clipboard && popupWindow.isSecureContext) { await clipboard.writeText(newJoinUrl); @@ -259,7 +259,6 @@ export function ShareWindow({ return; } } catch (fallbackError) { - // Continue to the manual-copy fallback below. } // Enable manual copying even when every copying method failed @@ -403,7 +402,7 @@ export function ShareWindow({ className="secondary" onClick={onClose} > - {intl.formatMessage(intlMessages.close)} + {intl.formatMessage(intlMessages.close)} From 02353341c5ce7aaff7d07fa2753002ec93368fc8 Mon Sep 17 00:00:00 2001 From: hiroshisuga <45039819+hiroshisuga@users.noreply.github.com> Date: Sun, 20 Sep 2026 19:03:59 +0900 Subject: [PATCH 13/27] Fix lint and formatting in the plugin component --- src/main/component.tsx | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/src/main/component.tsx b/src/main/component.tsx index 4e6fa08..c86327a 100644 --- a/src/main/component.tsx +++ b/src/main/component.tsx @@ -5,10 +5,10 @@ import { ActionsBarButton, ActionsBarInterface, ActionsBarPosition, ActionsBarSeparator, AppsGalleryEntry, BbbPluginSdk, PluginApi, } from 'bigbluebutton-html-plugin-sdk'; +import { createIntl, createIntlCache } from 'react-intl'; import { SessionSharePluginProps } from './types'; import { ShareWindow } from '../config-modal/modal'; -import { createIntl, createIntlCache } from 'react-intl'; import intlMessages from '../i18n'; const intlCache = createIntlCache(); @@ -148,20 +148,18 @@ function SessionSharePlugin({ ]); } else { // BBB 3.0 -> Action bar - const buttonToUserListItem: ActionsBarInterface = - new ActionsBarButton({ - icon: { - iconName: 'add', - }, - tooltip: label, - onClick: openShareWindow, - position: ActionsBarPosition.RIGHT, - }); - - const dropdownToUserListItem: ActionsBarInterface = - new ActionsBarSeparator({ - position: ActionsBarPosition.RIGHT, - }); + const buttonToUserListItem: ActionsBarInterface = new ActionsBarButton({ + icon: { + iconName: 'add', + }, + tooltip: label, + onClick: openShareWindow, + position: ActionsBarPosition.RIGHT, + }); + + const dropdownToUserListItem: ActionsBarInterface = new ActionsBarSeparator({ + position: ActionsBarPosition.RIGHT, + }); pluginApi.setActionsBarItems([ dropdownToUserListItem, From de31e607b8cabf7d68da0080a92c72a4481b4a2d Mon Sep 17 00:00:00 2001 From: hiroshisuga <45039819+hiroshisuga@users.noreply.github.com> Date: Sun, 20 Sep 2026 19:05:33 +0900 Subject: [PATCH 14/27] Update modal.tsx --- src/config-modal/modal.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/config-modal/modal.tsx b/src/config-modal/modal.tsx index ab57526..6ff646a 100644 --- a/src/config-modal/modal.tsx +++ b/src/config-modal/modal.tsx @@ -259,6 +259,7 @@ export function ShareWindow({ return; } } catch (fallbackError) { + // Continue to the manual-copy fallback below. } // Enable manual copying even when every copying method failed From 2fee8de895d4f3bc0b2f543cd3e7378508491d77 Mon Sep 17 00:00:00 2001 From: hiroshisuga <45039819+hiroshisuga@users.noreply.github.com> Date: Sun, 20 Sep 2026 19:08:26 +0900 Subject: [PATCH 15/27] Add alert for blocked popup notification --- src/main/component.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/component.tsx b/src/main/component.tsx index c86327a..9a8c4fc 100644 --- a/src/main/component.tsx +++ b/src/main/component.tsx @@ -74,6 +74,7 @@ function SessionSharePlugin({ ); if (!popup) { + // eslint-disable-next-line no-alert window.alert( intl.formatMessage(intlMessages.popupBlocked), ); From ca4c15f0a1a729bfae6e335afa29bf856be21482 Mon Sep 17 00:00:00 2001 From: hiroshisuga <45039819+hiroshisuga@users.noreply.github.com> Date: Sun, 20 Sep 2026 19:09:10 +0900 Subject: [PATCH 16/27] Update requiredSdkVersion to 0.0.73 --- manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifest.json b/manifest.json index 7ea6213..b4e559e 100644 --- a/manifest.json +++ b/manifest.json @@ -1,5 +1,5 @@ { - "requiredSdkVersion": "~0.0.59 || ^0.1.2", + "requiredSdkVersion": "~0.0.73 || ^0.1.2", "name": "BbbPluginSessionShare", "version": "0.0.2", "javascriptEntrypointUrl": "BbbPluginSessionShare.js", From e0539298f85842108c81461fe66bf28462efa6c9 Mon Sep 17 00:00:00 2001 From: hiroshisuga <45039819+hiroshisuga@users.noreply.github.com> Date: Sun, 20 Sep 2026 19:15:04 +0900 Subject: [PATCH 17/27] Delete src/config-modal/style.css --- src/config-modal/style.css | 182 ------------------------------------- 1 file changed, 182 deletions(-) delete mode 100644 src/config-modal/style.css diff --git a/src/config-modal/style.css b/src/config-modal/style.css deleted file mode 100644 index 08b7238..0000000 --- a/src/config-modal/style.css +++ /dev/null @@ -1,182 +0,0 @@ -.plugin-modal { - position: relative; - z-index: 1000 !important; - - outline: transparent; - outline-width: 2px; - outline-style: solid; - display: flex; - flex-direction: column; - padding: 2rem; - box-shadow: 0px 0px 20px rgba(0, 0, 0, 0.7); - background-color: #FFF !important; - max-width: 60vw; - max-height: 100%; - border-radius: .2rem; - overflow: auto; - - overflow-y: hidden; - - background-repeat: no-repeat; - background-color: transparent; - background-size: 100% 40px, 100% 40px, 100% 14px, 100% 14px; - background-attachment: local, local, scroll, scroll; - - &::-webkit-scrollbar { - width: 5px; - height: 5px; - } - &::-webkit-scrollbar-button { - width: 0; - height: 0; - } - &::-webkit-scrollbar-thumb { - background: rgba(0,0,0,.25); - border: none; - border-radius: 50px; - } - &::-webkit-scrollbar-thumb:hover { background: rgba(0,0,0,.5); } - &::-webkit-scrollbar-thumb:active { background: rgba(0,0,0,.25); } - &::-webkit-scrollbar-track { - background: rgba(0,0,0,.25); - border: none; - border-radius: 50px; - } - &::-webkit-scrollbar-track:hover { background: rgba(0,0,0,.25); } - &::-webkit-scrollbar-track:active { background: rgba(0,0,0,.25); } - &::-webkit-scrollbar-corner { background: 0 0; } - - @media only screen and (max-width: 40em) { - max-width: 95vw; - } - - @media only screen and (min-width: 40.063em) { - max-width: 80vw; - } -} - -.modal-overlay { - z-index: 1000; - display: flex; - align-items: center; - justify-content: center; - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(6, 23, 42, 0.75); -} - -.session-share-plugin { - .text { - font-size: 20px; - margin: 0.6rem auto; - } - - .select-options { - margin: 0.5rem auto; - } - - .buttons-wrapper { - width: 100%; - display: flex; - justify-content: space-around; - } - - - .button-style { - border: 3px solid transparent; - overflow: visible; - display: inline-block; - border-radius: 2px; - font-weight: 600; - line-height: 1; - text-align: center; - white-space: nowrap; - vertical-align: middle; - cursor: pointer; - user-select: none; - padding: 8px 15px; - } - - .button-style:hover { - border: 3px solid #0F70D7; - } - - .current-slide-text-container { - font-size: medium; - background-color: #aaa; - padding: 10px; - text-align: center; - border-radius: 5px; - margin-bottom: 10px; - } - - - /* Form styling */ - form { - width: 100%; - margin-top: 20px; - display: flex; - flex-direction: column; - align-items: center; - } - - /* Label and input field styling */ - label { - margin-bottom: 10px; /* Adds space between radio buttons */ - display: flex; - align-items: center; - } - - input[type="radio"] { - margin-right: 10px; /* Adds space between radio button and label text */ - } - - /* General button styling */ - button { - margin-top: 20px; - padding: 10px 20px; - cursor: pointer; - border: none; - color: white; - border-radius: 5px; - margin-right: 10px; /* Space between buttons */ - } - - /* Confirm button styling */ - button:first-child { - background-color: #007BFF; /* Green for Confirm */ - } - - /* Close button styling */ - button:last-child { - background-color: #022852; /* Red for Close, suggesting a cancellation or exit */ - margin-right: 0; - } - - - /* Button container */ - .buttons-container { - display: flex; - justify-content: center; - width: 100%; - } - - .copy-button-wrapper { - cursor: pointer; - margin-bottom: 15px; - } - - .share-url-field { - margin-inline: auto; - box-sizing: border-box; - } - - .error-message { - margin-top: 15px; - color: #b00020; - text-align: center; - } -} From 5751619a2a0d1538dd5532f46425be02430826e0 Mon Sep 17 00:00:00 2001 From: hiroshisuga <45039819+hiroshisuga@users.noreply.github.com> Date: Sun, 20 Sep 2026 19:18:50 +0900 Subject: [PATCH 18/27] Remove react-modal dependencies and bump version to 0.0.2 --- package-lock.json | 51 ++--------------------------------------------- package.json | 4 +--- 2 files changed, 3 insertions(+), 52 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2b273a1..ed4cade 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "bbb-plugin-session-share", - "version": "0.0.1", + "version": "0.0.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bbb-plugin-session-share", - "version": "0.0.1", + "version": "0.0.2", "dependencies": { "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", @@ -19,7 +19,6 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-intl": "^6.8.9", - "react-modal": "^3.16.1", "react-qr-code": "^2.0.12", "react-select": "^5.7.3" }, @@ -30,7 +29,6 @@ "@types/node": "^20.4.4", "@types/react": "^18.2.15", "@types/react-dom": "^18.2.7", - "@types/react-modal": "^3.16.0", "@types/styled-components": "^5.1.26", "@typescript-eslint/eslint-plugin": "^6.2.0", "@typescript-eslint/parser": "^6.2.0", @@ -2718,15 +2716,6 @@ "@types/react": "*" } }, - "node_modules/@types/react-modal": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/@types/react-modal/-/react-modal-3.16.0.tgz", - "integrity": "sha512-iphdqXAyUfByLbxJn5j6d+yh93dbMgshqGP0IuBeaKbZXx0aO+OXsvEkt6QctRdxjeM9/bR+Gp3h9F9djVWTQQ==", - "dev": true, - "dependencies": { - "@types/react": "*" - } - }, "node_modules/@types/react-transition-group": { "version": "4.4.6", "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.6.tgz", @@ -5427,11 +5416,6 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/exenv": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz", - "integrity": "sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw==" - }, "node_modules/express": { "version": "4.21.2", "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", @@ -8399,29 +8383,6 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, - "node_modules/react-lifecycles-compat": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", - "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" - }, - "node_modules/react-modal": { - "version": "3.16.1", - "resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.16.1.tgz", - "integrity": "sha512-VStHgI3BVcGo7OXczvnJN7yT2TWHJPDXZWyI/a0ssFNhGZWsPmB8cF0z33ewDXq4VfYMO1vXgiv/g8Nj9NDyWg==", - "dependencies": { - "exenv": "^1.2.0", - "prop-types": "^15.7.2", - "react-lifecycles-compat": "^3.0.0", - "warning": "^4.0.3" - }, - "engines": { - "node": ">=8" - }, - "peerDependencies": { - "react": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18", - "react-dom": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18" - } - }, "node_modules/react-qr-code": { "version": "2.0.12", "resolved": "https://registry.npmjs.org/react-qr-code/-/react-qr-code-2.0.12.tgz", @@ -10113,14 +10074,6 @@ "node": ">= 0.8" } }, - "node_modules/warning": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", - "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", - "dependencies": { - "loose-envify": "^1.0.0" - } - }, "node_modules/watch": { "version": "0.13.0", "resolved": "https://registry.npmjs.org/watch/-/watch-0.13.0.tgz", diff --git a/package.json b/package.json index ee4a4ae..b5ee934 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bbb-plugin-session-share", - "version": "0.0.1", + "version": "0.0.2", "private": true, "main": "./src/index.tsx", "dependencies": { @@ -15,7 +15,6 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-intl": "^6.8.9", - "react-modal": "^3.16.1", "react-qr-code": "^2.0.12", "react-select": "^5.7.3" }, @@ -46,7 +45,6 @@ "@types/node": "^20.4.4", "@types/react": "^18.2.15", "@types/react-dom": "^18.2.7", - "@types/react-modal": "^3.16.0", "@types/styled-components": "^5.1.26", "@typescript-eslint/eslint-plugin": "^6.2.0", "@typescript-eslint/parser": "^6.2.0", From 86953bb24da8ed231da49d41770ff9eccddadd92 Mon Sep 17 00:00:00 2001 From: hiroshisuga <45039819+hiroshisuga@users.noreply.github.com> Date: Sun, 20 Sep 2026 21:47:42 +0900 Subject: [PATCH 19/27] Implement fullscreen toggle for popup window Added fullscreen state management for the popup window. --- src/config-modal/modal.tsx | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/src/config-modal/modal.tsx b/src/config-modal/modal.tsx index 6ff646a..df507e4 100644 --- a/src/config-modal/modal.tsx +++ b/src/config-modal/modal.tsx @@ -153,6 +153,7 @@ export function ShareWindow({ const [loading, setLoading] = useState(false); const [errorMessage, setErrorMessage] = useState(''); const [copied, setCopied] = useState(false); + const [isFullscreen, setIsFullscreen] = useState(false); const shareUrlInputRef = React.useRef(null); let screenTitle = intl.formatMessage(intlMessages.shareOptions); @@ -193,6 +194,33 @@ export function ShareWindow({ } }, [popupWindow, newJoinUrl]); + React.useEffect(() => { + if (!popupWindow) { + setIsFullscreen(false); + return undefined; + } + + const popupDocument = popupWindow.document; + + const handleFullscreenChange = () => { + setIsFullscreen(Boolean(popupDocument.fullscreenElement)); + }; + + handleFullscreenChange(); + + popupDocument.addEventListener( + 'fullscreenchange', + handleFullscreenChange, + ); + + return () => { + popupDocument.removeEventListener( + 'fullscreenchange', + handleFullscreenChange, + ); + }; + }, [popupWindow]); + const handleConfirm = async () => { setLoading(true); setErrorMessage(''); @@ -395,7 +423,11 @@ export function ShareWindow({ type="button" onClick={handleFullscreen} > - {intl.formatMessage(intlMessages.fullScreen)} + {intl.formatMessage( + isFullscreen + ? intlMessages.exitFullScreen + : intlMessages.fullScreen, + )}