diff options
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/group-page.js')
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/static/group-page.js | 68 |
1 files changed, 52 insertions, 16 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/group-page.js b/packages/meshbay-hub/src/meshbay_hub/static/group-page.js index 45049a16..304a28fb 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/group-page.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/group-page.js @@ -247,6 +247,9 @@ function GroupPage({ groupId, group, token, username, userId, userPrefs, // In the Android application, when the local network was refused: whether // Android will still ask ("prompt") or only its settings can change it. const [lanBlocked, setLanBlocked] = useState(''); + // Set while the message is a reconnect refused the local network: the + // reconnect that gets through takes it away. + const lanLostRef = useRef(false); const transportRef = useRef(null); const gekRef = useRef(null); // One refresh per group: if a fresh token still says we are not a member, we @@ -394,6 +397,7 @@ function GroupPage({ groupId, group, token, username, userId, userPrefs, // Dropped by the teardown below, so a transport handed on to a running // download (`releaseWhenIdle`) stops driving a page that is gone. let offReconnect = null; + let offRefused = null; let deviceRetry = null; // The cache is written here and read only by the search page. It used to @@ -402,10 +406,27 @@ function GroupPage({ groupId, group, token, username, userId, userPrefs, // cache landing afterwards. Files shows what the node says, or says it // cannot reach the node. + // This device dropped the node's local addresses (transport.js + // localNetworkRefused): a setting to change here, not a node to wait for. + // Android 17 gates it per application, Chrome from 155. In the application, + // whether Android will still ask or only its settings can change it. + const explainLanRefusal = async () => { + if (!platform.localNetwork.available) { + return t(/Android/i.test(navigator.userAgent) + ? 'group.lan_blocked_android' : 'group.lan_blocked_browser'); + } + try { + const state = await platform.localNetwork.state(); + if (!cancelled) setLanBlocked(state === 'granted' ? '' : state); + } catch { /* the message stands alone */ } + return t('group.lan_blocked_app'); + }; + const connect = async () => { setStatus('discovering'); setError(''); setLanBlocked(''); + lanLostRef.current = false; // Belongs to the connection about to be made, not the group just left. setDeviceReady(false); gekRef.current = null; @@ -632,8 +653,24 @@ function GroupPage({ groupId, group, token, username, userId, userPrefs, // full fetch rather than a delta: this session was never told what it // missed, and a delta is computed against a snapshot only the node // has. + // Connected on mobile data, then on the node's Wi-Fi without the + // permission: every attempt is refused and the reconnect goes on, so a + // permission granted or mobile data again reaches the group by itself. + // Said while it lasts, over the view the group had. + offRefused = transport.addReconnectFailureListener(async (e) => { + if (cancelled || e.reason !== 'local_network_blocked') return; + const message = await explainLanRefusal(); + if (cancelled) return; + lanLostRef.current = true; + setError(message); + }); offReconnect = transport.addReconnectListener((reack) => { if (cancelled) return; + if (lanLostRef.current) { + lanLostRef.current = false; + setError(''); + setLanBlocked(''); + } (async () => { try { applyAck(reack); @@ -721,19 +758,9 @@ function GroupPage({ groupId, group, token, username, userId, userPrefs, if (err.reason === 'no_keys') setNeedsPass(true); // An identity sealed before the pepper: only the operator can clear it. if (err.reason === 'bundle_format_retired') err.message = t('group.bundle_format_retired'); - // This device dropped the node's local addresses (transport.js - // localNetworkRefused): a setting to change here, not a node to wait - // for. Android 17 gates it per application, Chrome from 155. - if (err.reason === 'local_network_blocked' && platform.localNetwork.available) { - err.message = t('group.lan_blocked_app'); - try { - const state = await platform.localNetwork.state(); - if (cancelled) return; - setLanBlocked(state === 'granted' ? '' : state); - } catch { /* the message stands alone */ } - } else if (err.reason === 'local_network_blocked') { - err.message = t(/Android/i.test(navigator.userAgent) - ? 'group.lan_blocked_android' : 'group.lan_blocked_browser'); + if (err.reason === 'local_network_blocked') { + err.message = await explainLanRefusal(); + if (cancelled) return; } // A key this node has never pinned, for an account it knows. The way in // is a device already trusted here, not an operator — which is the @@ -787,6 +814,7 @@ function GroupPage({ groupId, group, token, username, userId, userPrefs, return () => { cancelled = true; if (offReconnect) { offReconnect(); offReconnect = null; } + if (offRefused) { offRefused(); offRefused = null; } clearTimeout(deviceRetry); // Nothing will update this group's dock row once the page lets go of it. reportIndexPush(groupId, null); @@ -814,6 +842,14 @@ function GroupPage({ groupId, group, token, username, userId, userPrefs, // it; nothing in between should disturb a working connection. }, [groupId, Boolean(token), retryKey]); + // A connection still reconnecting is asked to try now rather than torn down: + // what it carries (a download, a film) goes on once it gets through. + const retry = useCallback(() => { + const transport = transportRef.current; + if (transport && transport.reconnecting) transport.retryNow(); + else setRetryKey((k) => k + 1); + }, []); + // A group that could not be reached is tried again when the device joins // another network: from the Wi-Fi to mobile data, or back, is what decides // whether the node can be reached, and in the Android application there is @@ -1061,12 +1097,12 @@ function GroupPage({ groupId, group, token, username, userId, userPrefs, <p class="settings-hint" style="margin-bottom:12px">${t('group.browser_access_off')}</p>`} ${error && html`<div class="error-msg" style="margin-bottom:12px">${error}${' '} <button class="admin-btn" style="margin-left:8px;font-size:0.9em" - onClick=${() => setRetryKey(k => k + 1)}>${t('group.retry')}</button> + onClick=${retry}>${t('group.retry')}</button> </div>`} ${lanBlocked === 'prompt' && html` <button class="admin-btn" style="margin-bottom:12px" onClick=${async () => { try { await platform.localNetwork.request(); } catch { /* retried either way */ } - setRetryKey(k => k + 1); + retry(); }}>${t('group.lan_allow')}</button>`} ${lanBlocked === 'denied' && html` <p class="settings-hint">${t('group.lan_settings_hint')}</p> @@ -1075,7 +1111,7 @@ function GroupPage({ groupId, group, token, username, userId, userPrefs, const back = () => { if (document.visibilityState !== 'visible') return; document.removeEventListener('visibilitychange', back); - setRetryKey(k => k + 1); + retry(); }; document.addEventListener('visibilitychange', back); platform.localNetwork.openSettings().catch(() => { |