tracker details

This commit is contained in:
Mateusz Gruszczyński
2026-06-05 13:26:29 +02:00
parent fefe3602eb
commit 88d956676e
3 changed files with 53 additions and 5 deletions
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
export const torrentTrackerDetailsSource = " function fmtTs(value){ const n=Number(value||0); if(!n) return '-'; try{return new Date(n*1000).toLocaleString();}catch(e){return String(n);} }\n function trackerSeedsPeers(t){ const hasScrape = t.seeds !== null || t.peers !== null; return hasScrape ? `${t.seeds ?? \"-\"} / ${t.peers ?? \"-\"}` : \"-\"; }\n function renderTrackers(trackers){\n // Note: Tracker URL editing is intentionally replaced by safe deletion; adding trackers remains unchanged.\n const pane=$('detailPane');\n const list=trackers||[];\n const canDelete=list.length>1;\n const rows=list.map(t=>{\n const idx=esc(t.index), url=esc(t.url);\n const deleteDisabled=canDelete ? '' : ' disabled title=\"At least one tracker must remain\"';\n return [`<span class=\"text-muted\">#${idx}</span>`, `<span class=\"tracker-url-text\">${url || '<span class=\"text-muted\">-</span>'}</span>`, t.enabled?'yes':'no', esc(trackerSeedsPeers(t)), esc(t.downloaded ?? '-'), fmtTs(t.last_announce), `<div class=\"tracker-actions\"><button class=\"btn btn-xs btn-outline-danger tracker-delete\" data-index=\"${idx}\"${deleteDisabled}><i class=\"fa-solid fa-trash\"></i> Delete</button></div>`];\n });\n // Note: Trackers share the responsive wrapper so long URLs do not break the details pane.\n pane.innerHTML=`<div class=\"tracker-toolbar\"><div class=\"input-group input-group-sm\"><input id=\"trackerAddUrl\" class=\"form-control tracker-add-input\" placeholder=\"https://tracker.example/announce\"><button id=\"trackerAddBtn\" class=\"btn btn-outline-primary\"><i class=\"fa-solid fa-plus\"></i> Add tracker</button></div><button id=\"trackerReannounceBtn\" class=\"btn btn-sm btn-outline-primary\"><i class=\"fa-solid fa-bullhorn\"></i> Reannounce</button></div>${responsiveTable(['#','URL','On','Seeds / Peers','Done','Last announce','Actions'], rows.length?rows:[[ '<span class=\"text-muted\">-</span>','<span class=\"text-muted\">No trackers.</span>','','','','','' ]], 'tracker-table')}`;\n }\n async function trackerAction(action,payload={}){\n if(!selectedHash) return toastMessage('toast.noTorrentSelected','warning');\n setBusy(true);\n try{\n const j=await post(`/api/torrents/${encodeURIComponent(selectedHash)}/trackers/${action}`,payload);\n toast(j.message || appMessage('toast.trackerActionDone',{action}),'success');\n await loadDetails('trackers');\n }catch(e){toast(e.message,'danger');}\n finally{setBusy(false);}\n }\n\n";
export const torrentTrackerDetailsSource = " function fmtTs(value){ const n=Number(value||0); if(!n) return '-'; try{return new Date(n*1000).toLocaleString();}catch(e){return String(n);} }\n function trackerSeedsPeers(t){ const hasScrape = t.seeds !== null || t.peers !== null; return hasScrape ? `${t.seeds ?? \"-\"} / ${t.peers ?? \"-\"}` : \"-\"; }\n function trackerUrlCell(t){\n const url=String(t.url||'').trim();\n // Note: Tracker URLs now use the same single-line ellipsis behavior as peer table cells.\n return url ? `<span class=\"tracker-url-text\" title=\"${esc(url)}\">${esc(url)}</span>` : '<span class=\"text-muted\">-</span>';\n }\n function trackerEnabledCell(enabled){\n // Note: Tracker enabled state is rendered as a compact badge to keep row height aligned with peers.\n return enabled ? '<span class=\"badge text-bg-success\">yes</span>' : '<span class=\"badge text-bg-secondary\">no</span>';\n }\n function renderTrackers(trackers){\n // Note: Tracker URL editing is intentionally replaced by safe deletion; adding trackers remains unchanged.\n const pane=$('detailPane');\n const list=trackers||[];\n const canDelete=list.length>1;\n const rows=list.map(t=>{\n const idx=esc(t.index);\n const deleteDisabled=canDelete ? '' : ' disabled title=\"At least one tracker must remain\"';\n return [`<span class=\"text-muted\">#${idx}</span>`, trackerUrlCell(t), trackerEnabledCell(t.enabled), esc(trackerSeedsPeers(t)), esc(t.downloaded ?? '-'), fmtTs(t.last_announce), `<div class=\"tracker-actions\"><button class=\"btn btn-xs btn-outline-danger tracker-delete\" data-index=\"${idx}\"${deleteDisabled}><i class=\"fa-solid fa-trash\"></i> Delete</button></div>`];\n });\n // Note: Trackers now use the same fixed responsive table pattern as peers for consistent row text and spacing.\n pane.innerHTML=`<div class=\"tracker-toolbar\"><div class=\"input-group input-group-sm\"><input id=\"trackerAddUrl\" class=\"form-control tracker-add-input\" placeholder=\"https://tracker.example/announce\"><button id=\"trackerAddBtn\" class=\"btn btn-outline-primary\"><i class=\"fa-solid fa-plus\"></i> Add tracker</button></div><button id=\"trackerReannounceBtn\" class=\"btn btn-sm btn-outline-primary\"><i class=\"fa-solid fa-bullhorn\"></i> Reannounce</button></div>${responsiveTable(['#','URL','On','Seeds / Peers','Done','Last announce','Actions'], rows.length?rows:[[ '<span class=\"text-muted\">-</span>','<span class=\"text-muted\">No trackers.</span>','','','','','' ]], 'tracker-table')}`;\n }\n async function trackerAction(action,payload={}){\n if(!selectedHash) return toastMessage('toast.noTorrentSelected','warning');\n setBusy(true);\n try{\n const j=await post(`/api/torrents/${encodeURIComponent(selectedHash)}/trackers/${action}`,payload);\n toast(j.message || appMessage('toast.trackerActionDone',{action}),'success');\n await loadDetails('trackers');\n }catch(e){toast(e.message,'danger');}\n finally{setBusy(false);}\n }\n\n";
+51 -3
View File
@@ -2221,8 +2221,55 @@ body.mobile-mode .mobile-filter-bar {
max-width: 520px;
}
.tracker-table {
min-width: 960px;
table-layout: fixed;
width: 100%;
}
.tracker-table th,
.tracker-table td {
overflow: hidden;
text-overflow: ellipsis;
vertical-align: middle;
}
.tracker-table th:nth-child(1),
.tracker-table td:nth-child(1) {
width: 6%;
}
.tracker-table th:nth-child(2),
.tracker-table td:nth-child(2) {
width: 38%;
}
.tracker-table th:nth-child(3),
.tracker-table td:nth-child(3),
.tracker-table th:nth-child(4),
.tracker-table td:nth-child(4),
.tracker-table th:nth-child(5),
.tracker-table td:nth-child(5) {
width: 10%;
}
.tracker-table th:nth-child(6),
.tracker-table td:nth-child(6) {
width: 16%;
}
.tracker-table th:nth-child(7),
.tracker-table td:nth-child(7) {
width: 10%;
}
.tracker-url-text {
word-break: break-all;
display: block;
max-width: 100%;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.tool-note {
@@ -5450,10 +5497,11 @@ body,
.mobile-details-files-table {
margin-bottom: 0;
min-width: 760px;
}
.mobile-details-files-table {
min-width: 760px;
.mobile-details-trackers-table {
margin-bottom: 0;
}
.mobile-details-files-table .file-progress {