You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

97 lines
3.8 KiB

{{define "content"}}
<article class="ebooks-page">
<header class="page-header">
<h1>E-Books</h1>
<p class="page-summary">Top-level publications (index events) from Nostr. These are publications that are not contained in any higher-level index event.</p>
</header>
<div class="ebooks-container">
<table id="ebooks-table" class="ebooks-table" aria-label="E-Books listing">
<thead>
<tr>
<th data-sort="title" class="sortable">Title <span class="sort-indicator"></span></th>
<th data-sort="author" class="sortable">Author <span class="sort-indicator"></span></th>
</tr>
</thead>
<tbody>
{{range .EBooks}}
<tr>
<td>
<strong>{{.Title}}</strong>
{{if .Summary}}<br><small class="text-muted">{{.Summary}}</small>{{end}}
<div style="margin-top: 0.75rem;">
<a href="https://alexandria.gitcitadel.eu/publication/naddr/{{.Naddr}}" target="_blank" rel="noopener noreferrer" class="btn btn-sm"><span class="icon-inline">{{icon "external-link"}}</span> View on Alexandria</a>
</div>
</td>
<td>{{template "user-badge-simple" (dict "Pubkey" .Author "Profiles" $.Profiles)}}</td>
</tr>
{{else}}
<tr>
<td colspan="2" class="text-center"><span class="icon-inline">{{icon "book-x"}}</span> No e-books found.</td>
</tr>
{{end}}
</tbody>
</table>
</div>
</article>
<script>
(function() {
const table = document.getElementById('ebooks-table');
if (!table) return;
const tbody = table.querySelector('tbody');
const headers = table.querySelectorAll('th.sortable');
let currentSort = { column: null, direction: 'asc' };
headers.forEach(header => {
header.addEventListener('click', function() {
const column = this.dataset.sort;
const direction = currentSort.column === column && currentSort.direction === 'asc' ? 'desc' : 'asc';
// Update sort indicators
headers.forEach(h => {
const indicator = h.querySelector('.sort-indicator');
if (h === this) {
indicator.textContent = direction === 'asc' ? '↑' : '↓';
} else {
indicator.textContent = '↕';
}
});
// Sort rows
const rows = Array.from(tbody.querySelectorAll('tr'));
rows.sort((a, b) => {
let aVal, bVal;
const aCell = a.cells[Array.from(headers).indexOf(this)];
const bCell = b.cells[Array.from(headers).indexOf(this)];
if (column === 'title') {
aVal = aCell.querySelector('strong')?.textContent || '';
bVal = bCell.querySelector('strong')?.textContent || '';
} else {
aVal = aCell.textContent.trim();
bVal = bCell.textContent.trim();
}
if (direction === 'asc') {
return aVal.localeCompare(bVal);
} else {
return bVal.localeCompare(aVal);
}
});
// Re-append sorted rows
rows.forEach(row => tbody.appendChild(row));
currentSort = { column, direction };
});
// Add cursor pointer style
header.style.cursor = 'pointer';
});
})();
</script>
{{end}}
{{/* Feed is defined in components.html */}}