diff --git a/packages/scratch-gui/src/containers/connection-modal.jsx b/packages/scratch-gui/src/containers/connection-modal.jsx index 5b62439e453..76bfea68065 100644 --- a/packages/scratch-gui/src/containers/connection-modal.jsx +++ b/packages/scratch-gui/src/containers/connection-modal.jsx @@ -45,6 +45,10 @@ class ConnectionModal extends React.Component { PHASES.connected : (props.extensionId === 'meshV2' ? PHASES.meshV2Initial : PHASES.scanning); // === Smalruby: End of meshV2 initial step feature === + // Track whether the user explicitly changed the domain input. + // When false and the user clicks Create/Join, we clear the cached + // domain so createDomain() auto-detects from source IP. + this.userChangedDomain = false; this.state = { extension: extensionData.find(ext => ext.extensionId === props.extensionId), phase: initialPhase, @@ -201,6 +205,9 @@ class ConnectionModal extends React.Component { } // === Smalruby: Start of meshV2 initial step feature === handleMeshV2CreateGroup () { + // If domain input is empty, clear any cached domain from localStorage + // so that createDomain() will be called to auto-detect from source IP + this.clearMeshV2DomainIfEmpty(); // Connect as host using special host ID this.handleConnecting('meshV2_host'); analytics.event({ @@ -210,6 +217,9 @@ class ConnectionModal extends React.Component { }); } handleMeshV2JoinGroup () { + // If domain input is empty, clear any cached domain from localStorage + // so that createDomain() will be called to auto-detect from source IP + this.clearMeshV2DomainIfEmpty(); // Switch to scanning phase to show group list this.handleScanning(); analytics.event({ @@ -218,7 +228,20 @@ class ConnectionModal extends React.Component { label: this.props.extensionId }); } + clearMeshV2DomainIfEmpty () { + // If user did not explicitly change the domain input in this modal + // session, clear any cached domain from localStorage so that + // createDomain() will auto-detect from source IP. + if (!this.userChangedDomain) { + const extension = this.props.vm.runtime.peripheralExtensions.meshV2; + if (extension && extension.setDomain) { + extension.setDomain(null); + } + this.props.onDomainChange(null); + } + } handleMeshV2DomainChange (domain) { + this.userChangedDomain = true; // Save domain to Redux this.props.onDomainChange(domain); diff --git a/packages/scratch-gui/test/unit/containers/connection-modal.test.jsx b/packages/scratch-gui/test/unit/containers/connection-modal.test.jsx index 3f06756d9ef..01d0c7ef7db 100644 --- a/packages/scratch-gui/test/unit/containers/connection-modal.test.jsx +++ b/packages/scratch-gui/test/unit/containers/connection-modal.test.jsx @@ -140,6 +140,27 @@ describe('ConnectionModal container', () => { }); }); + describe('meshV2 domain handling on modal open', () => { + test('shows meshV2Initial phase when not connected', () => { + const vm = createMockVm({isConnected: false}); + const {getByTestId} = renderWithStore(vm, {domain: 'cached-domain'}); + + // Should show meshV2Initial phase (domain input visible) + expect(getByTestId('phase').textContent).toBe('meshV2Initial'); + }); + + test('preserves cached domain in Redux when modal opens', () => { + const {setDomain} = require('../../../src/reducers/mesh-v2'); + setDomain.mockClear(); + + const vm = createMockVm({isConnected: false}); + renderWithStore(vm, {domain: 'cached-domain'}); + + // Should NOT reset domain on modal open (preserves user's previous input) + expect(setDomain).not.toHaveBeenCalledWith(null); + }); + }); + describe('handleConnected updates connectedMessage', () => { test('updates connectedMessage from vm after PERIPHERAL_CONNECTED event', () => { const vm = createMockVm({