Skip to content

Commit ef72bd0

Browse files
committed
fix(check_port): Fix layout bug by rendering UI dynamically
Previously, if both IP versions were enabled in the configuration, the plugin would render HTML elements for both, even if the system only had connectivity for one. The unused elements were then hidden with CSS, which caused a layout bug where the Flexbox gap property created unwanted spacing. This commit refactors init.js to build the UI completely dynamically on each update. The getPortStatus function now checks for the actual availability of each IP address at runtime before creating and appending its corresponding DOM elements (the .port-group). This ensures that only the necessary HTML is rendered, resulting in a cleaner DOM and definitively fixing the layout spacing issue regardless of the user's network connectivity.
1 parent 24f8ebf commit ef72bd0

File tree

2 files changed

+64
-105
lines changed

2 files changed

+64
-105
lines changed

plugins/check_port/check_port.css

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,16 @@
2222
overflow: hidden;
2323
}
2424

25-
/* 3. IP Text Behavior */
25+
/* 3. Group for each IP version (icon + text) */
26+
.port-group {
27+
display: flex;
28+
align-items: center;
29+
gap: 3px; /* Space between icon and text inside the group */
30+
overflow: hidden; /* Prevent internal content from breaking out */
31+
min-width: 0; /* Allow the group to shrink */
32+
}
33+
34+
/* 4. IP Text Behavior */
2635
.port-status-container .port-ip-text-segment {
2736
/* Prevents long text from wrapping and breaking the flexbox layout */
2837
white-space: nowrap;
@@ -32,7 +41,7 @@
3241
min-width: 0;
3342
}
3443

35-
/* 4. Icon Base and Status Styles */
44+
/* 5. Icon Base and Status Styles */
3645
.port-status-container .icon {
3746
background-position: center;
3847
background-repeat: no-repeat;
@@ -42,14 +51,17 @@
4251
flex-shrink: 0;
4352
}
4453

54+
/* Port status is unknown */
4555
.port-status-container .pstatus0 {
4656
background-image: var(--pane-port-unknown-icon);
4757
}
4858

59+
/* Port is closed (error state) */
4960
.port-status-container .pstatus1 {
5061
background-image: var(--pane-port-error-icon);
5162
}
5263

64+
/* Port is open (success state) */
5365
.port-status-container .pstatus2 {
5466
background-image: var(--pane-port-success-icon);
5567
}

plugins/check_port/init.js

Lines changed: 50 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -10,28 +10,14 @@ const a = {};
1010
* @param {boolean} isUpdate - If true, indicates a manual refresh, adding "..." to the title
1111
*/
1212
plugin.resetStatus = function(isUpdate) {
13-
// Reset icons to the "unknown" state (pstatus0) and ensure they are visible for the loading state
14-
a.iconIPv4.removeClass().addClass("icon pstatus0").show();
15-
a.iconIPv6.removeClass().addClass("icon pstatus0").show();
16-
17-
// Hide IP address text and the separator
18-
a.textIPv4.text("").hide();
19-
a.separator.text("").hide();
20-
a.textIPv6.text("").hide();
21-
2213
// Set a tooltip to indicate that a check is in progress
2314
let title = theUILang.checkingPort || "Checking port status...";
2415
if (isUpdate) {
2516
title += "..."; // Append ellipsis for manual updates
2617
}
27-
a.pane.prop("title", title);
28-
};
29-
30-
// Initial check when the plugin is first loaded
31-
plugin.init = function() {
32-
plugin.resetStatus(false);
33-
// Request the initial port status from the backend
34-
theWebUI.request("?action=initportcheck", [plugin.getPortStatus, plugin]);
18+
if (a.pane) {
19+
a.pane.prop("title", title);
20+
}
3521
};
3622

3723
// Function to manually trigger an update of the port status
@@ -41,76 +27,57 @@ plugin.update = function() {
4127
theWebUI.request("?action=updateportcheck", [plugin.getPortStatus, plugin]);
4228
};
4329

44-
/**
45-
* Updates the UI for a specific IP protocol (IPv4 or IPv6) based on data from the backend
46-
* @param {object} data - The response data containing status for both protocols
47-
* @param {string} proto - The protocol to update, either "ipv4" or "ipv6"
48-
* @param {function} getStatusText - A function to retrieve the localized status string
49-
* @returns {string} The formatted title line for this protocol's status
50-
*/
51-
function updateProtocolStatus(data, proto, getStatusText) {
52-
const isEnabled = data['use_' + proto];
53-
const icon = (proto === 'ipv4') ? a.iconIPv4 : a.iconIPv6;
54-
const textEl = (proto === 'ipv4') ? a.textIPv4 : a.textIPv6;
55-
56-
// Handle the case where the protocol is not enabled in conf.php
57-
if (!isEnabled) {
58-
icon.hide();
59-
textEl.hide();
60-
return ""; // Return an empty title line if not enabled
61-
}
62-
63-
const status = parseInt(data[proto + '_status']);
64-
const address = data[proto];
65-
const port = data[proto + '_port'];
66-
const isAvailable = address && address !== "-"; // Check if an IP address was returned
67-
68-
// Update the icon class to reflect the current status
69-
icon.removeClass("pstatus0 pstatus1 pstatus2").addClass("pstatus" + status);
70-
71-
let titleText = "";
72-
73-
if (isAvailable) {
74-
icon.show();
75-
// Format display text as IP:PORT, with brackets for IPv6
76-
const displayText = (proto === 'ipv6') ? `[${address}]:${port}` : `${address}:${port}`;
77-
textEl.text(displayText).show();
78-
// Create a detailed title for the tooltip
79-
titleText = `${proto.toUpperCase()}: ${displayText} (${getStatusText(status)})`;
80-
} else {
81-
// If IP is not available on the server, hide the icon and the text element
82-
icon.hide();
83-
textEl.hide();
84-
// Still provide a title for debugging or information
85-
titleText = `${proto.toUpperCase()}: ${(theUILang.notAvailable || "N/A")}`;
86-
}
87-
return titleText;
88-
}
89-
9030
/**
9131
* Main callback to process the port status response from the backend and update the UI
9232
* @param {object} d - The JSON object received from the backend response
9333
*/
9434
plugin.getPortStatus = function(d) {
95-
// Helper function to get the localized text for a status code
35+
// Always clear the pane first to rebuild the UI dynamically
36+
a.pane.empty();
37+
9638
const getStatusText = (statusCode) => theUILang.portStatus[statusCode] || theUILang.portStatus[0] || "Unknown";
39+
const isIPv4Available = d.ipv4 && d.ipv4 !== "-";
40+
const isIPv6Available = d.ipv6 && d.ipv6 !== "-";
41+
const titleLines = [];
42+
43+
// Conditionally create and append the IPv4 group only if the IP is available
44+
if (isIPv4Available) {
45+
const status = parseInt(d.ipv4_status);
46+
const displayText = `${d.ipv4}:${d.ipv4_port}`;
47+
const ipv4Group = $("<div>").attr("id", "port-group-ipv4").addClass("port-group");
48+
ipv4Group.append($("<div>").attr("id", "port-icon-ipv4").addClass("icon pstatus" + status));
49+
ipv4Group.append($("<span>").attr("id", "port-ip-text-ipv4").addClass("d-none d-lg-block port-ip-text-segment").text(displayText));
50+
a.pane.append(ipv4Group);
51+
titleLines.push(`IPV4: ${displayText} (${getStatusText(status)})`);
52+
} else if (d.use_ipv4) {
53+
titleLines.push(`IPV4: ${(theUILang.notAvailable || "N/A")}`);
54+
}
55+
56+
// Conditionally create and append the separator
57+
if (isIPv4Available && isIPv6Available) {
58+
a.pane.append($("<span>").attr("id", "port-ip-separator").addClass("d-none d-lg-block").text("|"));
59+
}
9760

98-
// Update the status for both IPv4 and IPv6 and collect their title lines
99-
const titleLines = [
100-
updateProtocolStatus(d, 'ipv4', getStatusText),
101-
updateProtocolStatus(d, 'ipv6', getStatusText)
102-
].filter(line => line); // Filter out empty strings for disabled/unavailable protocols
103-
104-
// Show a separator only if both protocol icons are visible
105-
// The CSS 'gap' property will handle the spacing automatically
106-
if (a.iconIPv4.is(":visible") && a.iconIPv6.is(":visible")) {
107-
a.separator.text("|").show();
108-
} else {
109-
a.separator.text("").hide();
61+
// Conditionally create and append the IPv6 group only if the IP is available
62+
if (isIPv6Available) {
63+
const status = parseInt(d.ipv6_status);
64+
const displayText = `[${d.ipv6}]:${d.ipv6_port}`;
65+
const ipv6Group = $("<div>").attr("id", "port-group-ipv6").addClass("port-group");
66+
ipv6Group.append($("<div>").attr("id", "port-icon-ipv6").addClass("icon pstatus" + status));
67+
ipv6Group.append($("<span>").attr("id", "port-ip-text-ipv6").addClass("d-none d-lg-block port-ip-text-segment").text(displayText));
68+
a.pane.append(ipv6Group);
69+
titleLines.push(`IPV6: ${displayText} (${getStatusText(status)})`);
70+
} else if (d.use_ipv6) {
71+
titleLines.push(`IPV6: ${(theUILang.notAvailable || "N/A")}`);
11072
}
11173

112-
// Set the combined tooltip for the entire status pane
74+
// Set the final combined tooltip for the entire status pane
11375
a.pane.prop("title", titleLines.join(" | "));
76+
77+
// Re-attach the context menu handler since we cleared the pane
78+
if (plugin.canChangeMenu()) {
79+
a.pane.off("mousedown", plugin.createPortMenu).on("mousedown", plugin.createPortMenu);
80+
}
11481
};
11582

11683
// Defines the AJAX request for the initial port check
@@ -139,36 +106,16 @@ plugin.createPortMenu = function(e) {
139106
};
140107

141108
plugin.onLangLoaded = function() {
142-
// Create status bar elements in a more readable way
143-
const container = $("<div>").addClass("port-status-container");
144-
145-
const ipv4Icon = $("<div>").attr("id", "port-icon-ipv4").addClass("icon");
146-
const ipv4Text = $("<span>").attr("id", "port-ip-text-ipv4").addClass("d-none d-lg-block port-ip-text-segment");
147-
const separator = $("<span>").attr("id", "port-ip-separator").addClass("d-none d-lg-block");
148-
const ipv6Icon = $("<div>").attr("id", "port-icon-ipv6").addClass("icon");
149-
const ipv6Text = $("<span>").attr("id", "port-ip-text-ipv6").addClass("d-none d-lg-block port-ip-text-segment");
109+
// Create a temporary loading state immediately
110+
const container = $("<div>").addClass("port-status-container")
111+
.append($("<div>").addClass("icon pstatus0")); // Add a single "unknown" icon as a placeholder
150112

151-
// Assemble the elements into the container
152-
container.append(ipv4Icon, ipv4Text, separator, ipv6Icon, ipv6Text);
153-
154-
// Add the newly created pane to the ruTorrent status bar
155113
plugin.addPaneToStatusbar("port-pane", container, -1, true);
156-
157-
// Now that the pane is in the DOM, cache all the jQuery elements for future use
158114
a.pane = $("#port-pane");
159-
a.iconIPv4 = $("#port-icon-ipv4");
160-
a.textIPv4 = $("#port-ip-text-ipv4");
161-
a.separator = $("#port-ip-separator");
162-
a.iconIPv6 = $("#port-icon-ipv6");
163-
a.textIPv6 = $("#port-ip-text-ipv6");
164-
165-
// If the user has permissions, attach the right-click context menu
166-
if (plugin.canChangeMenu()) {
167-
a.pane.on("mousedown", plugin.createPortMenu);
168-
}
115+
a.pane.prop("title", theUILang.checkingPort || "Checking port status...");
169116

170-
// Trigger the initial port check
171-
plugin.init();
117+
// Trigger the initial port check to get the configuration and build the final UI
118+
theWebUI.request("?action=initportcheck", [plugin.getPortStatus, plugin]);
172119
};
173120

174121
// This function is called when the plugin is removed/unloaded

0 commit comments

Comments
 (0)