Fix latency-probe races, stale offline menu, and byte formatting

This commit is contained in:
av
2026-07-18 15:36:40 +03:00
parent 629e031845
commit 7a8c2f238c
3 changed files with 35 additions and 8 deletions
+29 -5
View File
@@ -35,6 +35,8 @@ class SingBoxIndicator extends PanelMenu.Button {
this._selectorItems = new Map();
this._proxiesMap = {};
this._delays = new Map(); // node name -> ms number | 'timeout'
this._probing = new Set(); // nodes with a latency probe in flight
this._apiUrl = '';
this._polling = false;
this._cancellable = new Gio.Cancellable();
@@ -131,14 +133,18 @@ class SingBoxIndicator extends PanelMenu.Button {
// ---- Settings -----------------------------------------------------
_onSettingsChanged() {
// Latencies measured against one instance are meaningless for another.
if (this._settings.get_string('api-url') !== this._apiUrl)
this._delays.clear();
this._applySettings();
this._restartPolling();
this._poll();
}
_applySettings() {
this._apiUrl = this._settings.get_string('api-url');
this._api.setEndpoint(
this._settings.get_string('api-url'),
this._apiUrl,
this._settings.get_string('api-secret')
);
this._interval = this._settings.get_int('refresh-interval');
@@ -299,6 +305,9 @@ class SingBoxIndicator extends PanelMenu.Button {
entry.setOrnament(isCurrent
? PopupMenu.Ornament.DOT
: PopupMenu.Ornament.NONE);
// Don't overwrite the "…" placeholder while a probe is running.
if (this._probing.has(member))
continue;
entry.label.text = this._memberLabelText(member, map[member]);
}
}
@@ -340,6 +349,16 @@ class SingBoxIndicator extends PanelMenu.Button {
}
}
// Drop menu content that would otherwise stay clickable while offline.
_clearMenuData() {
this._selectorsSection.removeAll();
this._selectorItems.clear();
this._selectorSignature = null;
this._probing.clear();
this._connItem.menu.removeAll();
this._connItem.label.text = `${_('Connections')}: —`;
}
_pickPanelGroup(selectors) {
if (selectors.length === 0)
return null;
@@ -384,6 +403,7 @@ class SingBoxIndicator extends PanelMenu.Button {
if (!stored)
return;
for (const [member, item] of stored.members) {
this._probing.add(member);
item.label.text = `${member} · …`;
this._api.getDelay(member, {}, this._cancellable)
.then(res => {
@@ -394,9 +414,14 @@ class SingBoxIndicator extends PanelMenu.Button {
this._delays.set(member, 'timeout');
})
.finally(() => {
this._probing.delete(member);
if (this._cancellable.is_cancelled())
return;
item.label.text = this._memberLabelText(member, this._proxiesMap?.[member]);
// Re-resolve the row: the menu may have been rebuilt while
// the probe was in flight, disposing the captured actor.
const cur = this._selectorItems.get(groupName)?.members.get(member);
if (cur)
cur.label.text = this._memberLabelText(member, this._proxiesMap?.[member]);
});
}
}
@@ -423,13 +448,12 @@ class SingBoxIndicator extends PanelMenu.Button {
break;
case STATE.OFFLINE:
this._dotFilled = false;
this._statusItem.label.text = error
? _('sing-box · Unreachable')
: _('sing-box · Offline');
this._statusItem.label.text = _('sing-box · Unreachable');
this._outboundLabel.text = '';
this._speedLabel.text = '';
this._speedItem.label.text = _('API unreachable — check URL and secret in Settings');
this._prevSample = null;
this._clearMenuData();
break;
default:
this._dotFilled = false;