diff --git a/links/templates/links/link_list.html b/links/templates/links/link_list.html index 22b4a5a..c380302 100644 --- a/links/templates/links/link_list.html +++ b/links/templates/links/link_list.html @@ -208,6 +208,10 @@ .apple-btn-primary:hover { background: var(--apple-blue-hover); color: #fff; } .apple-btn-danger { background: transparent; color: var(--apple-red); border: 1px solid rgba(255, 59, 48, 0.4); } .apple-btn-danger:hover { background: rgba(255, 59, 48, 0.08); } + .apple-btn-danger:disabled { + opacity: 0.45; color: var(--apple-gray); border-color: rgba(0, 0, 0, 0.12); + cursor: default; pointer-events: none; background: transparent; + } /* ---------- Link library (table) ---------- */ .library-header { @@ -579,7 +583,19 @@ /* ---------- Small screens ---------- */ @media (max-width: 640px) { .library-header { padding: 1.35rem 1.1rem 1rem; } - .library-footer { padding: 1rem 1.1rem 1.25rem; } + .library-footer { + position: sticky; bottom: 0; z-index: 5; + background: rgba(245, 245, 247, 0.88); + -webkit-backdrop-filter: blur(20px) saturate(180%); + backdrop-filter: blur(20px) saturate(180%); + border-top: 1px solid rgba(0, 0, 0, 0.05); + border-radius: 0 0 var(--apple-radius-lg) var(--apple-radius-lg); + margin: 0 -1.1rem -1.25rem; + padding: 0.7rem 1.1rem calc(0.7rem + env(safe-area-inset-bottom, 0px)); + } + /* Search takes the full row on mobile (it is the primary interaction) */ + .library-filter { flex: 1 1 100%; } + .library-filter input { width: 100%; height: 48px; font-size: 1rem; } .apple-table thead th, .apple-table tbody td { padding-left: 0.8rem; padding-right: 0.8rem; } .hero-stats { gap: 1.25rem; } .posts-panel-header { padding: 1.15rem 1.1rem 0.6rem; } @@ -965,7 +981,7 @@ {% endif %} @@ -1406,6 +1422,7 @@ /* ---------- Load more links ---------- */ const loadMoreBtn = document.getElementById('load-more-links'); + const tbody = document.getElementById('links-table-body'); if (loadMoreBtn && tbody) { loadMoreBtn.addEventListener('click', function () { const offset = loadMoreBtn.dataset.offset; @@ -1444,6 +1461,8 @@ if (!selectionHint) return; const count = document.querySelectorAll('input[name="selected_links"]:checked').length; selectionHint.textContent = count > 0 ? count + ' {% trans "selected" %}' : ''; + const delBtn = document.getElementById('delete-selected-btn'); + if (delBtn) delBtn.disabled = count === 0; } if (selectAll) { diff --git a/links/views.py b/links/views.py index ab84216..17901a0 100644 --- a/links/views.py +++ b/links/views.py @@ -47,7 +47,7 @@ from .search_views import search_aliases, search logger = logging.getLogger(__name__) # Number of link rows rendered on the initial home page load / per "Load more" click. -LINKS_PAGE_SIZE = 50 +LINKS_PAGE_SIZE = 20 def _ordering_for(sort_by, order): @@ -74,14 +74,14 @@ class LinkListView(ListView): def get_queryset(self): queryset = super().get_queryset() - sort_by = self.request.GET.get('sort', 'click_count') - order = self.request.GET.get('order', 'asc') + sort_by = self.request.GET.get('sort', 'updated_at') + order = self.request.GET.get('order', 'desc') return queryset.order_by(_ordering_for(sort_by, order)) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context['current_sort'] = self.request.GET.get('sort', 'click_count') - context['current_order'] = self.request.GET.get('order', 'asc') + context['current_sort'] = self.request.GET.get('sort', 'updated_at') + context['current_order'] = self.request.GET.get('order', 'desc') # Modern color palette colors = [ @@ -128,8 +128,8 @@ def load_more_links(request): """Return the next page of link rows (rendered as HTML) for the home page's "Load more" button, so the initial page load doesn't have to render the entire link table at once.""" - sort_by = request.GET.get('sort', 'click_count') - order = request.GET.get('order', 'asc') + sort_by = request.GET.get('sort', 'updated_at') + order = request.GET.get('order', 'desc') try: offset = max(int(request.GET.get('offset', 0)), 0) except (TypeError, ValueError):