| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
|
|
| |
- Point README badges and ROADMAP query at the krazywarez_hutch SonarCloud project
- Change user-facing contact emails to [email protected] (README, SECURITY, About screen)
- Bump MARKETING_VERSION to 3.8.5 and CURRENT_PROJECT_VERSION to 93
|
| |
|
|
| |
The 3.8.3 train is closed for new submissions on App Store Connect.
|
| | |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
Splash only highlights Swift, leaving every other language unhighlighted.
Drop the dependency entirely and render file contents as plain text until a
broader-coverage highlighter is adopted.
- Remove `import Splash` and the Splash-backed theming in FileTreeView
- Simplify CodeSyntaxHighlighter to emit plain attributed text
- Remove the Splash SwiftPM package reference and product dependency
Refs https://github.com/zerolabsco/hutch/issues/16
Co-Authored-By: Claude Opus 4.8 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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.
|
| |
|
|
|
|
| |
Update the roadmap's SonarCloud section to the live 53-issue / 10-rule
reality and mark what v3.8.1 fixed, silenced-as-bug, and left Won't Fix.
Bump MARKETING_VERSION on the app, widget, and Safari extension.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
MARKETING_VERSION 3.7.0 -> 3.8.0, build 89 -> 90.
SCOPE.md gains the items that did not survive contact with the API: archiveMessage
and mailingListSubscribe are blocked, while webhooks, shareSecret, and build
groups are reachable but declined on judgement. The reasoning is recorded so
they do not get re-proposed as gaps.
ROADMAP.md notes that Phase 3 is several releases rather than one, with the
measured size of each.
|
| |
|
|
|
|
|
| |
MARKETING_VERSION 3.6.0 -> 3.7.0, build 88 -> 89.
The README feature list also picks up Phase 1's ticket editing, subscriptions,
and email preferences, which it never gained.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
* refactor: collapse duplicated request paths in SRHTClient
Five request paths each repeated the token guard, header setup, status-code
handling, and a ~35-line #if DEBUG logging block. The file carried that block
five times over.
Extract makeAuthorizedRequest, send, and encodedGraphQLBody, and route execute,
executeAndCache, executeMultipartFiles, and performGraphQLRequest through them.
executeMultipart is now the single-file case of executeMultipartFiles, which it
already was byte for byte. 938 lines to 612, with one copy of the logging block.
fetchText keeps its own guard: it is a GET to an allowlisted URL and must not
run GraphQL error checks over what is usually a plain-text build log.
One behavior change falls out. executeAndCache wrote the raw response to the
cache before decoding, so a 200 carrying GraphQL errors was cached and then
thrown. Routing it through performGraphQLRequest surfaces those errors first,
so error payloads are no longer cached.
* feat: edit and delete tickets
updateTicket and deleteTicket both existed in todo.sr.ht's API but were never
called, so a ticket could be filed and its status changed but its subject and
body were frozen from the moment it was created, and it could never be removed.
Edit opens a sheet seeded with the current subject and body. The input carries
only fields that actually changed, so an edit cannot clobber a field the user
did not touch, and Save stays disabled until something differs.
Clearing the body sends an explicit null via updateValue rather than a nil
subscript assignment, which would drop the key and silently leave the old body
in place — the same trap fixed for repository descriptions in 7ffef07.
Delete is destructive and irreversible, so it sits behind a confirmation
dialog naming the ticket and pops the detail view on success.
* feat: subscribe to and unsubscribe from tickets
ticketSubscribe and ticketUnsubscribe existed in the API but were never called,
so email notifications for a ticket could only be managed on the web.
Ticket.subscription is null when the user is not subscribed, so the detail
query now reads it and the menu reflects real server state rather than
guessing. The toggle updates optimistically and reverts on failure, so the
control never claims a subscription that did not take.
Decoded into the private payload rather than TicketDetail, which is Codable and
cached — adding a field there would have changed the cached shape and touched
every optimistic-update construction site.
* feat: subscribe to and unsubscribe from trackers
trackerSubscribe and trackerUnsubscribe existed in the API but were never
called. Tracker.subscription is null when not subscribed, so the state can be
read rather than guessed.
The read is a separate uncached query. The tickets query it sits beside is
paginated and cached, and a per-user subscription has no business riding along
in page payloads or being served stale from disk.
Unsubscribe passes tickets: false, so leaving a tracker does not silently drop
subscriptions to individual tickets the user opted into.
* feat: unsubscribe from mailing lists
mailingListUnsubscribe existed in the API but was never called, so the list of
subscriptions was readable and nothing more.
Scoped to unsubscribe. MailingList has no subscription field, unlike Ticket and
Tracker, so per-list state is only knowable from the subscriptions query — which
is exactly what builds this view. Subscribing would need a list the user is by
definition not subscribed to, and sr.ht exposes no discovery API to find one
(see SCOPE.md on hub.sr.ht), so there is nowhere honest to put that action yet.
The row is removed optimistically and restored if the mutation fails. The
confirmation says plainly that Hutch cannot resubscribe, since it cannot.
* feat: manage todo and lists email preferences
updatePreferences existed on both services but was never called, so these were
web-only settings.
The two services expose preferences/updatePreferences under identical names but
with different fields — notifySelf on todo, copySelf on lists — and there is no
shared preferences service, so both are read and written side by side. They load
concurrently and one service being unreachable does not hide the other's toggle.
These are server-side and apply beyond Hutch, unlike the @AppStorage toggles
above them in Settings, so the footer says so and each toggle reverts if its
mutation fails.
* refactor: drop the memory-only cache path
Two executeCached overloads existed with different return types and semantics:
one doing stale-while-revalidate against the persistent cache with TTLs, the
other only consulting the in-memory responseCache. The second was an easy thing
to reach for by mistake, since the compiler picked it purely on argument labels.
It turned out to be dead. All 38 call sites already used the TTL-aware
overload, and the memory-only one was the sole caller of executeAndCache, so
both are removed. Its doc comment promised refresh "via the onRefresh callback",
which the signature has not had for some time.
SRHTClient is now 569 lines, down from 938 before this branch.
responseCache stays as the in-memory layer behind cachedPayload and the three
view models that read it directly.
* chore: bump to 3.6.0 and record Phase 1
MARKETING_VERSION 3.5.0 -> 3.6.0, build 87 -> 88.
* fix: decode preferences responses on the main actor
The module sets SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor, so the response
types are implicitly main-actor isolated and their Decodable conformances are
too. Decoding straight from an `async let` used those conformances from a
nonisolated context, which warns today and is an error in the Swift 6 language
mode.
Move each fetch into its own method and `async let` over those instead, so
decoding stays on the main actor. This is what HomeViewModel.loadDashboard
already does, and the concurrency is unaffected — the network work still
overlaps, since execute suspends and frees the actor.
|
| |\
| |
| |
| |
| | |
zerolabsco/dependabot/swift/github.com/apple/swift-markdown-0.8.0
chore(deps): bump github.com/apple/swift-markdown from 0.7.3 to 0.8.0
|
| | |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
Bumps [github.com/apple/swift-markdown](https://github.com/apple/swift-markdown) from 0.7.3 to 0.8.0.
- [Release notes](https://github.com/apple/swift-markdown/releases)
- [Commits](https://github.com/apple/swift-markdown/compare/0.7.3...3c6f9523da3a1ec2fd829673e472d95b8097a3b8)
---
updated-dependencies:
- dependency-name: github.com/apple/swift-markdown
dependency-version: 0.8.0
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot] <[email protected]>
|
| |/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Bumps [github.com/swiftlang/swift-cmark](https://github.com/swiftlang/swift-cmark) from 0.7.1 to 0.8.0.
- [Release notes](https://github.com/swiftlang/swift-cmark/releases)
- [Changelog](https://github.com/swiftlang/swift-cmark/blob/gfm/changelog.txt)
- [Commits](https://github.com/swiftlang/swift-cmark/compare/0.7.1...924936d0427cb25a61169739a7660230bffa6ea6)
---
updated-dependencies:
- dependency-name: github.com/swiftlang/swift-cmark
dependency-version: 0.8.0
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot] <[email protected]>
|
| |
|
|
|
|
|
|
|
|
| |
The Hutch scheme referenced `container:HutchTests`, omitting the
`.xctestplan` extension, so `xcodebuild test -scheme Hutch` failed with
"the test plan HutchTests could not be read". HutchTests.xcscheme already
used the correct reference.
The README directs contributors to the Hutch scheme, so this path needs to
work before it can be wired into CI.
|
| | |
|
| | |
|
| | |
|
| |
|
|
|
|
|
|
|
|
|
|
| |
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.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Introduce an actor-backed persistent cache layer at the SRHTClient boundary
for read-only SourceHut data. Cache entries now store stable metadata
including key, resource type, fetched/expires/access timestamps, payload
hash, schema version, and payload size, with bounded memory and disk usage.
Add centralized cache key builders and TTL defaults for repository, file,
ticket, build, log, profile, status, and list-style resources. Support
networkOnly, cacheOnly, cacheFirstThenRefresh, and refreshIgnoringCache
policies, plus request coalescing for duplicate in-flight cache keys.
Integrate first-pass caching into high-value low-risk read paths:
- build detail and completed/active build logs
- ticket detail
- README lookup
- repository tree, blob, and linked file reads
Keep mutation paths network-only and add simple prefix invalidation after
ticket and build mutations. Add compact cached/stale UI status rows and a
Settings action to clear the persistent cache.
Add focused cache tests covering round trips, expiration, stale fallback,
policy behavior, request coalescing, prefix invalidation, size limits, LRU
pruning, expired pruning, and mutation bypass behavior. Document storage,
key, TTL, invalidation, limitations, and next recommended targets.
|
| |
|
|
| |
Fixes: https://todo.sr.ht/~ccleberg/hutch/69
|
| | |
|
| |
|
|
|
|
|
|
|
| |
- 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
|
| |
|
|
|
|
| |
of implementing its own view
Fixes: https://todo.sr.ht/~ccleberg/hutch/66
|
| | |
|
| | |
|
| |
|
|
|
|
| |
tapped
Fixes: https://todo.sr.ht/~ccleberg/hutch/63
|
| |
|
|
| |
Implements: https://todo.sr.ht/~ccleberg/hutch/64
|
| | |
|
| |
|
|
|
|
|
|
| |
- removed prototype screens
- limited Recent section to 3 items
- added initial support for tips
Fixes: https://todo.sr.ht/~ccleberg/hutch/65
|
| | |
|
| | |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
- Add shared HutchDeepLinkURL constants for app, widgets, and tests.
- Bump marketing version to 3.1.2 and build to 70 (app + widget extension).
- Point Hutch stats default base URL at HutchStatsAPI; add SRHTWebURL status
incident feed and reuse it from SystemStatusService.
- Group Project into Metadata and Resources; split ContributionStatsResponse
into StatsWindow and StatsTotals with updated decoding and tests.
- Replace @Bindable usage with explicit Bindings in Profile, Projects list,
and repository ACL flows; simplify Home pinned-item helper; add no-op
bodies on cancel alert buttons where the compiler requires a statement.
- Move repository row build-status indicator next to the relative-updated
caption and reserve a fixed 8×8 slot so the row does not jump when status
loads.
- Use NSString.lastPathComponent for build artifact filenames; collapse
duplicate ACL error branches; minor HutchStats HTTP and XMLParserDelegate
cleanups.
- Point widgets at HutchDeepLinkURL helpers; align tests with the new
response and URL types.
|
| |
|
|
|
|
|
|
| |
todo.sr.ht only exposes cursor-based `tracker.tickets` (no status filter),
so client-side Open filtering missed older open tickets when only the first
page was loaded. Paginate until the cursor is exhausted in loadTickets().
Fixes: https://todo.sr.ht/~ccleberg/hutch/62
|
| |
|
|
|
|
|
|
|
|
| |
Apply listRowBackground(Color.black) per row in AMOLED mode using ThemedRowStyle
and themedRow() across Lists and Forms. Use themedList() for scroll/grouped
backgrounds. Treat segmented and clear list rows with isAMOLED-aware
listRowBackground where Color.clear was required. Removes reliance on UIKit
appearance for list cells on iOS 16+.
Implements: https://todo.sr.ht/~ccleberg/hutch/24
|
| |
|
|
|
|
|
|
|
|
|
|
| |
Replaces the list-based layout with a full-screen viewer that matches
the repository file tree pattern. File contents are now the primary
focus using CodeFileTextView (line numbers, wrap toggle). A compact
sticky header shows the filename, visibility badge, owner, date, and
abbreviated hash. Paste metadata moves to a Details sheet accessible
via the bottom toolbar. Duplicate share buttons are consolidated: link
sharing lives in the ··· menu and content sharing is in the toolbar.
Fixes: https://todo.sr.ht/~ccleberg/hutch/13
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
When scrolling through a large tracker, SwiftUI's .task modifier cancels
in-flight pagination requests as rows leave the screen. URLSession
responds with URLError(-999), which was being surfaced to the user as
"The network request failed. Please try again." — even though the
cancellation is expected and benign.
Guard against Task.isCancelled in the catch block so cancellations are
silently ignored; isLoadingMore is still reset unconditionally so the
next scroll attempt retries from the same cursor position.
Fixes: https://todo.sr.ht/~ccleberg/hutch/61
|
| |
|
|
|
|
|
|
|
|
|
| |
git.sr.ht returns "internal system error" from the references query and
"reference not found" from the log/readme queries when a repo has no
commits. Broaden isEmptyRepositoryError to cover missingReference,
unknownRevision, noRows, notFound, and those two message strings, then
apply the same silent-empty treatment to loadReferences and loadArtifacts
(which previously surfaced any error directly to the user).
Fixes: https://todo.sr.ht/~ccleberg/hutch/60
|
| |
|
|
|
|
|
|
|
|
| |
- implements consistent UX between repo types
- ensures centralized services and routes are used between repo types
- graphql error handling is centralized
- error messages are consistent
Implements: https://todo.sr.ht/~ccleberg/hutch/58
Implements: https://todo.sr.ht/~ccleberg/hutch/59
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Convert filteredJobs, filteredPastes, and filteredTickets from computed
properties to stored properties updated via didSet on their inputs. Each
update function guards with an equality check before assigning, so
@Observable skips the notification when the filtered result hasn't
changed — preventing list re-renders on auto-refresh when no visible
data has actually changed.
Add Equatable conformance to BuildRowView, PasteRowView, TicketRowView,
and SelectableTicketRow, and apply .equatable() at each ForEach call
site. When a list does re-render, SwiftUI now skips body evaluation for
rows whose model value is identical to the previous pass.
Implements: https://todo.sr.ht/~ccleberg/hutch/57
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
- 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
|
| |
|
|
|
|
| |
Implements: https://todo.sr.ht/~ccleberg/hutch/52
Implements: https://todo.sr.ht/~ccleberg/hutch/53
Implements: https://todo.sr.ht/~ccleberg/hutch/54
|
| |
|
|
|
| |
Implements: https://todo.sr.ht/~ccleberg/hutch/47
Implements: https://todo.sr.ht/~ccleberg/hutch/48
|
| |
|
|
|
| |
Implements: https://todo.sr.ht/~ccleberg/hutch/45
Implements: https://todo.sr.ht/~ccleberg/hutch/46
|
| |
|
|
|
|
| |
Implements: https://todo.sr.ht/~ccleberg/hutch/42
Implements: https://todo.sr.ht/~ccleberg/hutch/43
Implements: https://todo.sr.ht/~ccleberg/hutch/44
|
| |
|
|
|
|
| |
Implements: https://todo.sr.ht/~ccleberg/hutch/39
Implements: https://todo.sr.ht/~ccleberg/hutch/40
Implements: https://todo.sr.ht/~ccleberg/hutch/41
|
| |
|
|
|
|
| |
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/29
Implements: https://todo.sr.ht/~ccleberg/hutch/30
|
| |
|
|
|
|
| |
Implements: https://todo.sr.ht/~ccleberg/hutch/26
Implements: https://todo.sr.ht/~ccleberg/hutch/27
Implements: https://todo.sr.ht/~ccleberg/hutch/28
|
| |
|
|
|
| |
Refs: https://todo.sr.ht/~ccleberg/hutch/24
Implements: https://todo.sr.ht/~ccleberg/hutch/25
|
| |
|
|
| |
fixes: https://todo.sr.ht/~ccleberg/hutch/19
|
| |
|
|
|
| |
implements: https://todo.sr.ht/~ccleberg/hutch/17
implements: https://todo.sr.ht/~ccleberg/hutch/18
|