| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Replace the disruption-only banner on the Home tab with a persistent
circular status indicator in the navigation bar, next to the "Home"
title. The badge reflects the existing status logic — green/check when
operational, orange/exclamation on disruption, gray otherwise — and taps
through to System Status. SystemStatusSummaryRow is unchanged and still
used by the More tab.
Resolve the system-status result first in HomeViewModel.loadDashboard so
the badge settles from cache immediately instead of waiting on the
slower projects/jobs/tickets/inbox loads, which had left it spinning for
several seconds.
Bump MARKETING_VERSION to 3.8.2 and record it in ROADMAP.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
loadDashboard(forceRefresh:) fanned the flag out to five loaders, but
loadProjects and loadSystemStatusSnapshot dropped it: they called
fetchProjects() and snapshotResult() with no policy, so pull-to-refresh
returned cached projects and status while the other three sections
refreshed. SonarCloud flagged both params as unused (swift:S1172).
Thread forceRefresh through ProjectService.fetchProjects into the page
policy (refreshIgnoringCache when forced), and pass it to
snapshotResult, which already accepted it. ProjectsListView carried the
same latent bug via its own .refreshable — fixed there too now that
fetchProjects can force.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Thread.updated is the root email's insert time. It never advances when a reply
arrives, despite the name and despite the schema describing threads as ordered
"most recently bumped". sr.ht returns updated seven seconds after root.date on
a thread carrying four replies:
subject: "Re: test post"
updated: 2026-07-16T03:44:55Z
replies: 4
root: { date: 2026-07-15T22:44:48-05:00 }
The app fed that field into lastActivityAt everywhere, so a reply to an old
thread was invisible: it did not mark the thread unread, did not raise it in the
list, and did not update the timestamp beside it.
This was inert until the unread baseline landed, because an absent view record
returned unread unconditionally and the date was never compared to anything. The
baseline did not break unread; it was the first code to depend on that field
meaning what it says.
Use MailingList.emails instead, which is real reverse-chronological arrival
data, keyed by thread root id. The scan stops once it is older than the cutoff,
so a quiet list costs one page and a busy one costs only what is new. Threads
with nothing in the window keep the root timestamp and stay read, which they
already were. Failure returns empty activity rather than throwing: unread is a
decoration and should not take the thread list down with it.
Uses Email.received rather than Email.date — received is server-side and
non-null, date comes from the sender's header and is neither.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
New mail showed as unread inside a mailing list but not on Home.
The list view reads threads with client.execute, so it always sees the server.
Home reads them with executeCached under cacheFirstThenRefresh, which returns
the cached value and only schedules a background fetch. loadDashboard had no way
to bypass that, so pulling to refresh could not surface mail that arrived after
the last fetch — it returned the same cache it already had and Home stayed a
cycle behind.
Thread forceRefresh through loadDashboard to the cached reads, and use it from
the pull to refresh on Home and Work. Matches loadSystemStatus(forceRefresh:),
which MoreView already refreshes this way.
The staleness predates the unread baseline. It was invisible while every thread
counted as unread, because Home always had a large number to show; now that Home
correctly ignores mail older than sign-in, a stale cache reads as a truthful 0.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
InboxThreadSummary.id returned threadGroupingKey, which is listRID plus the
subject with Re:/Fwd: stripped. Two unrelated threads on one list sharing a
subject therefore shared an id — common on sourcehut, where "[PATCH] test" is
an ordinary subject — which collides under Identifiable in every list that
renders these summaries.
Key id on the root Message-ID, which is unique per thread, and leave
threadGroupingKey subject-based so replies still collapse into one
conversation.
Read state moves to threadGroupingKey at each call site. It was already
keyed on that string via id, so persisted keys are unchanged and marking a
conversation read still covers the whole subject group, matching how
HomeViewModel already builds the key for isUnread.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
Add a bounded stale-while-revalidate cache at the Sourcehut API boundary with
stable keys, centralized TTLs, request coalescing, payload hashing, and LRU disk
pruning.
Cache high-value read-only repo, build, ticket, project, profile, paste, and
Home/Work Queue data while keeping mutations network-only and invalidating
related prefixes after successful writes.
Add focused cache tests and implementation notes.
|
| | |
|
| | |
|
| | |
|
| |
|
|
|
|
|
|
|
| |
- limit Home failed-build counts to a configurable lookback window
and add the setting under Behavior.
- make the Recent and Builds rows fully tappable across the
entire cell and add coverage for the new failed-build filtering.
Fixes: https://todo.sr.ht/~ccleberg/hutch/67
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
- Remove no-op per-row task from RepositoryListView; loadMoreIfNeeded
is a stub for repos so each row was allocating a Task that did nothing
- Fix BuildListView auto-refresh stopping permanently after navigating
away; startAutoRefresh now runs unconditionally on task so it restarts
on every reappear, not just first load
- Add lastRefreshed tracking to HomeViewModel with a needsRefresh(after:)
helper; HomeView and WorkView scene-activation handlers now skip
loadDashboard() if the data is less than 60 seconds old
- Add 120-second TTL to repository build status refresh; statuses are
no longer re-fetched on every tab appear, only when stale or when the
user explicitly pulls to refresh (forceRefresh: true)
Implements: https://todo.sr.ht/~ccleberg/hutch/56
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Reworks the app's core navigation and home screen for v3.0.0:
- Replace Inbox with Work view: unified dashboard showing unread threads
and assigned tickets, with All/Unread/Assigned scope picker
- Overhaul Home screen: redesigned with pinned items grid (trackers,
repos, mailing lists, users), recent activity section, and system
status banner; backed by HomePinStore supporting all resource types
- Simplify navigation bars across list screens: default toolbar shows
only the primary action (+) and an overflow menu (…); pin, share, and
select moved into the overflow menu; selection mode gets its own nav
bar with Cancel / "N Selected" / All
- Consolidate repo detail toolbars: pin and share moved into the
existing actions menu for both Git and Mercurial detail views
- Add recent activity tracking via RecentActivityStore
- Add work and inbox deep link aliases (hutch://work, hutch://inbox)
Implements: https://todo.sr.ht/~ccleberg/hutch/55
|
| |
|
|
|
|
| |
Implements: https://todo.sr.ht/~ccleberg/hutch/49
Implements: https://todo.sr.ht/~ccleberg/hutch/50
Implements: https://todo.sr.ht/~ccleberg/hutch/51
|
| |
|
|
|
|
| |
Refs: https://todo.sr.ht/~ccleberg/hutch/36
Refs: https://todo.sr.ht/~ccleberg/hutch/37
Refs: https://todo.sr.ht/~ccleberg/hutch/38
|
| |
|
|
|
|
| |
Implements: https://todo.sr.ht/~ccleberg/hutch/26
Implements: https://todo.sr.ht/~ccleberg/hutch/27
Implements: https://todo.sr.ht/~ccleberg/hutch/28
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| |
|