Compare commits

..

No commits in common. "v0.6.2" and "v0.6.1" have entirely different histories.

4 changed files with 15 additions and 31 deletions

View file

@ -9,13 +9,6 @@ former name, "Daybook".)
_Nothing yet._ _Nothing yet._
## [0.6.2] — 2026-09-07
### Changed
- Source sidebar is now ordered by **most recent message** (Telegram-style)
instead of alphabetically; "All sources" stays pinned at the top. Live
updates move a source to the top when it receives a new message.
## [0.6.1] — 2026-09-07 ## [0.6.1] — 2026-09-07
### Changed ### Changed
@ -221,8 +214,7 @@ First tagged release.
- Dockerised (Gunicorn + whitenoise), SQLite, `git`-based deploy with a - Dockerised (Gunicorn + whitenoise), SQLite, `git`-based deploy with a
runbook (`deploy/cloud_agent_deploy.md`) and health check. runbook (`deploy/cloud_agent_deploy.md`) and health check.
[Unreleased]: https://git.cygnus.systems/peter/daybook/compare/v0.6.2...HEAD [Unreleased]: https://git.cygnus.systems/peter/daybook/compare/v0.6.1...HEAD
[0.6.2]: https://git.cygnus.systems/peter/daybook/compare/v0.6.1...v0.6.2
[0.6.1]: https://git.cygnus.systems/peter/daybook/compare/v0.6.0...v0.6.1 [0.6.1]: https://git.cygnus.systems/peter/daybook/compare/v0.6.0...v0.6.1
[0.6.0]: https://git.cygnus.systems/peter/daybook/compare/v0.5.1...v0.6.0 [0.6.0]: https://git.cygnus.systems/peter/daybook/compare/v0.5.1...v0.6.0
[0.5.1]: https://git.cygnus.systems/peter/daybook/compare/v0.5.0...v0.5.1 [0.5.1]: https://git.cygnus.systems/peter/daybook/compare/v0.5.0...v0.5.1

View file

@ -19,7 +19,7 @@ from django.contrib.auth import views as auth_views
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.db import OperationalError, transaction from django.db import OperationalError, transaction
from django.db.models import Count, Max, Q, Sum from django.db.models import Count, Q, Sum
from django.http import FileResponse, Http404, HttpResponse, HttpResponseForbidden, JsonResponse from django.http import FileResponse, Http404, HttpResponse, HttpResponseForbidden, JsonResponse
from django.shortcuts import get_object_or_404, redirect, render from django.shortcuts import get_object_or_404, redirect, render
from django.template.defaultfilters import date as date_filter, slugify from django.template.defaultfilters import date as date_filter, slugify
@ -586,12 +586,10 @@ def feed(request):
source_scoped = apply_feed_filters( source_scoped = apply_feed_filters(
live, "", selected_label, selected_kind, title_query live, "", selected_label, selected_kind, title_query
) )
# Sources ordered by most recent message first (Telegram-style); "All
# sources" is rendered separately and stays pinned at the top.
source_counts = ( source_counts = (
source_scoped.values("source") source_scoped.values("source")
.annotate(count=Count("id", filter=Q(read_at__isnull=True)), latest=Max("created_at")) .annotate(count=Count("id", filter=Q(read_at__isnull=True)))
.order_by("-latest") .order_by("source")
) )
keep_count = live.filter(is_kept=True).count() keep_count = live.filter(is_kept=True).count()
trash_count = Artifact.objects.filter(owner=request.user, deleted_at__isnull=False).count() trash_count = Artifact.objects.filter(owner=request.user, deleted_at__isnull=False).count()

View file

@ -1,4 +1,4 @@
# Single source of truth for the app version. Bump this, commit, then tag the # Single source of truth for the app version. Bump this, commit, then tag the
# commit `vX.Y.Z` and deploy. Surfaced on Settings -> About and in # commit `vX.Y.Z` and deploy. Surfaced on Settings -> About and in
# GET /api/capabilities ("version"). # GET /api/capabilities ("version").
__version__ = "0.6.2" __version__ = "0.6.1"

View file

@ -443,17 +443,15 @@
`<span class="source-name">${escapeHtml(name)}</span>` + `<span class="source-name">${escapeHtml(name)}</span>` +
`<span class="source-count">${count}</span>`; `<span class="source-count">${count}</span>`;
// Newest activity goes to the top of the per-source list (just under // Insert alphabetically among the per-source links (after "All sources").
// the pinned "All sources"), matching the recency ordering.
const list = document.querySelector(".source-list"); const list = document.querySelector(".source-list");
const firstSource = list.querySelector(".source-link:not(.all-sources-link)"); const others = sourceLinks().filter(
list.insertBefore(link, firstSource); (l) => l.querySelector(".source-name")?.textContent.trim() !== "All sources",
}; );
const after = others.find(
const moveSourceToTop = (link) => { (l) => (l.querySelector(".source-name")?.textContent.trim() || "") > name,
const list = document.querySelector(".source-list"); );
const firstSource = list.querySelector(".source-link:not(.all-sources-link)"); list.insertBefore(link, after || null);
if (firstSource !== link) list.insertBefore(link, firstSource);
}; };
// From the newly-arrived rows: bump each source's unread count (adding a // From the newly-arrived rows: bump each source's unread count (adding a
@ -468,12 +466,8 @@
Object.entries(counts).forEach(([source, n]) => { Object.entries(counts).forEach(([source, n]) => {
total += n; total += n;
const existing = findSourceLink(source); const existing = findSourceLink(source);
if (existing) { if (existing) bumpSourceBadge(existing, n);
bumpSourceBadge(existing, n); else addSourceLink(source, n);
moveSourceToTop(existing); // most-recent activity -> top (recency order)
} else {
addSourceLink(source, n);
}
}); });
const allLink = findSourceLink("All sources"); const allLink = findSourceLink("All sources");
if (allLink && total) bumpSourceBadge(allLink, total); if (allLink && total) bumpSourceBadge(allLink, total);