Commit graph

84 commits

Author SHA1 Message Date
Stefan Hausotte
ad35a6f4f7 docs: add a CHANGELOG.md file
Add a CHANGELOG.md file that uses the "keep a changelog" convention to
make changes to Forji more transparent
2026-06-04 14:44:51 +02:00
Stefan Hausotte
0bcb397805 chore: bump Forji version to 1.5 2026-06-04 14:39:31 +02:00
Stefan Hausotte
330d2bde4f fix: remove redundant notification Dismiss swipe action (#46)
The trailing Dismiss swipe called the same setNotificationRead path as the
leading Mark Read action, since ForgejoKit's NotificationService only exposes
markAsRead (PATCH to-status=read). Dismiss therefore did nothing distinct and
left the row visible under the All/Read filters, just relabeled.

Drop the redundant action in both NotificationsOverviewView and
MergedNotificationsOverviewView. The leading Mark Read swipe (shown for unread
threads, full-swipe by default) remains the single, honest action.

Reviewed-on: https://codeberg.org/secana/Forji/pulls/64
2026-06-04 14:08:20 +02:00
Stefan Hausotte
745b7af45b fix: persist instance removal before deleting keychain on logout (#48)
logout deleted keychain credentials first, then removed the SwiftData instance
with a swallowed `try? modelContext.save()`. If the save failed, the instance
survived with its credentials gone, leaving an account that could not
authenticate. A `nil` default modelContext also let the instance removal be
skipped entirely while credentials were still wiped.

Reorder to delete + save the SwiftData instance first (matching
InstanceListView.deleteInstance) and only delete credentials once that succeeds;
bail out on save failure so the account stays usable. Make modelContext
required to remove the latent orphan path (all call sites already pass one).

Reviewed-on: https://codeberg.org/secana/Forji/pulls/65
2026-06-04 14:08:07 +02:00
Stefan Hausotte
d95ec8ddc5 fix: refresh issue/PR detail after a partial edit failure (#50)
IssueEditView and PullRequestEditView save by running several API calls in
sequence (replaceLabels, editIssue, editPullRequest, requestReviewers,
removeReviewers). Forgejo has no transaction, so if a later call fails the
earlier ones stay committed, yet the catch block only showed an error and never
fired onSaved, leaving the detail view with stale data.

On failure, re-fetch the issue/PR and push the authoritative server state via
onSaved (without dismissing, so the user can retry) before surfacing the error.
True rollback is not possible against the REST API; reflecting the real
committed state is the correct remedy.

Reviewed-on: https://codeberg.org/secana/Forji/pulls/66
2026-06-04 14:07:53 +02:00
secana
8e7f99b6e3 fix: settle merged pagination and drop duplicate rows (#47) 2026-06-04 14:07:32 +02:00
Stefan Hausotte
bec427d7da fix: settle merged pagination and drop duplicate rows (#47)
PaginationState computed hasMore as `fetched.count >= pageSize`, but merged
views return the combined, de-duplicated result of N instances, so the merged
count cleared the single-source threshold and kept hasMore true past the real
end (extra empty fetches). loadMore also appended without de-duping against
already-loaded items, so overlapping involvement queries and shifting pages
could produce duplicate TaggedItem.id entries in the ForEach.

Add an optional dedupeKey to PaginationState that de-duplicates across pages via
a running seen-set, and base hasMore on whether a page contributed new items
when dedupeKey is set. Single-source pagination keeps the page-size heuristic
unchanged. Merged Issues/PRs, Repositories, and Notifications views opt in with
dedupeKey = { $0.id }.
2026-06-04 13:50:40 +02:00
Stefan Hausotte
66fe573cb6 chore: update ForgejoKit dependency to 0.7.0 2026-06-04 13:10:43 +02:00
systemBlue
c2454d3444 docs: add CONTRIBUTING.md (#42) 2026-06-04 13:01:59 +02:00
systemBlue
ed2051ae5d docs: fix typo in README App Store line (#43) 2026-06-04 13:01:41 +02:00
systemBlue
cbd4039e40 feat: add VoiceOver accessibility labels to icon-only buttons (#49) 2026-06-04 13:01:22 +02:00
systemBlue
e9b6be91f5 fix: don't open a duplicate PR when the reviewer request fails (#53) 2026-06-04 13:00:44 +02:00
systemBlue
4f6803cc03 fix: don't crash the background notification poll on an invalid instance URL (#54) 2026-06-04 13:00:25 +02:00
systemBlue
639812348b fix: refresh the merged issue/PR overview after a mutation (#56) 2026-06-04 12:59:59 +02:00
systemBlue
4ef76cf2d7 fix: update the app icon badge in multi-instance mode (#58) 2026-06-04 12:59:43 +02:00
systemBlue
8e56b9d722 feat: make tappable diff lines act as buttons for VoiceOver (#61) 2026-06-04 12:59:17 +02:00
Stefan Hausotte
b0f50eca38 Merge branch 'main' of ssh://codeberg.org/secana/Forji 2026-06-02 18:21:07 +02:00
systemBlue
31d9aeea35 fix: Delete the API token, not just the password, when removing an account (#40)
`deleteInstance` cleared only the keychain password, never the token (`logout` deletes both), so swipe-to-delete left a live API token orphaned; this routes both paths through a new `KeychainManager.deleteCredentials` and adds regression tests (186 ForjiTests green).

I grant Stefan Hausotte an irrevocable, worldwide, royalty-free license to use, sublicense, and distribute my contribution, including through Apple's App Store under the project's App Store exception.

Reviewed-on: https://codeberg.org/secana/Forji/pulls/40
2026-06-02 18:20:43 +02:00
Stefan Hausotte
8648f40c07 fix: guard that prevents edit of accounts to add the same account 2026-06-02 18:20:12 +02:00
systemBlue
89a0cd0bb2 fix: crash merged overviews when two accounts share a sourceKey (#39)
Adding the same server and username twice gives two connections one `sourceKey`, and `rehydrate`'s `Dictionary(uniqueKeysWithValues:)` traps on it, crashing every merged overview on construction; this keeps the first source and adds a regression test (185 ForjiTests green).

I grant Stefan Hausotte an irrevocable, worldwide, royalty-free license to use, sublicense, and distribute my contribution, including through Apple's App Store under the project's App Store exception.

Reviewed-on: https://codeberg.org/secana/Forji/pulls/39
2026-06-02 18:05:53 +02:00
systemBlue
c6dda2cd70 fix: read messages not marked read
Fixes #32. Opening a notification now marks its thread read (via the detail's `.onAppear`) so the tab and icon badges clear; swipe actions unchanged. Adds a UI regression test covering open-to-clear.

I grant Stefan Hausotte an irrevocable, worldwide, royalty-free license to use, sublicense, and distribute my contribution, including through Apple's App Store under the project's App Store exception.
2026-06-02 18:01:07 +02:00
Stefan Hausotte
992c628abd refactor: fix linting issues 2026-05-17 21:40:51 +02:00
Stefan Hausotte
6faf3ad986 chore: update ForgejoKit to 0.6.1 2026-05-17 21:40:36 +02:00
pdurlej
a93130ee09 refactor: use ForgejoKit error categories (#31)
Problem
- SessionRestoreError now duplicates HTTP status-code classification that ForgejoKit exposes in 0.6.0.

Change
- Uses ForgejoKit's httpErrorCategory/httpStatusCode for auth and service restore failures.
- Keeps the same user-facing SessionRestoreError outcomes.
- Leaves network and certificate handling unchanged.

Tests
- DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer xcodebuild -quiet -project Forji/Forji.xcodeproj -scheme Forji -destination "platform=iOS Simulator,name=iPhone 17 Pro,OS=26.4.1" build-for-testing -only-testing:ForjiTests/SessionRestoreErrorTests
- DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer xcodebuild -quiet -project Forji/Forji.xcodeproj -scheme Forji -destination "platform=iOS Simulator,name=iPhone 17 Pro,OS=26.4.1" test-without-building -only-testing:ForjiTests/SessionRestoreErrorTests

Co-authored-by: Piotr Durlej <pdurlej@users.noreply.github.com>
Reviewed-on: https://codeberg.org/secana/Forji/pulls/31
2026-05-17 21:29:12 +02:00
Stefan Hausotte
075669d87e test: fix warning in test 2026-05-12 18:20:09 +02:00
Stefan Hausotte
fd85a211f6 chore: update ForgejoKit to 0.6.0 2026-05-12 18:19:58 +02:00
pdurlej
5e22431d11 fix: preserve token restore error context (#30)
Problem
- Token-only session restore currently reports every token validation failure as an expired/revoked token, including permission, server, network, and invalid-response failures.

Change
- Preserves token validation error context for token-only instances.
- Maps auth/service/network failures to more specific user-facing restore errors.
- Leaves password fallback behavior unchanged for password-based instances.

Tests
- DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer xcodebuild -quiet -project Forji/Forji.xcodeproj -scheme Forji -destination "platform=iOS Simulator,name=iPhone 17 Pro,OS=26.4.1" build-for-testing -only-testing:ForjiTests/SessionRestoreErrorTests
- DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer xcodebuild -quiet -project Forji/Forji.xcodeproj -scheme Forji -destination "platform=iOS Simulator,name=iPhone 17 Pro,OS=26.4.1" test-without-building -only-testing:ForjiTests/SessionRestoreErrorTests

Co-authored-by: Piotr Durlej <pdurlej@users.noreply.github.com>
Reviewed-on: https://codeberg.org/secana/Forji/pulls/30
Reviewed-by: secana <secana@noreply.codeberg.org>
2026-05-12 17:45:02 +02:00
Stefan Hausotte
db4e54db6b chore: add "just sim-update" command
Updates the iOS simulators to the latest version in XCode
2026-05-10 11:01:40 +02:00
pdurlej
0b815335a9 fix: key multi-instance fallback by account (#29)
## Summary

This changes the multi-instance fallback map to key bootstrapped connections by account instead of server URL only.

Previously, `connect(instances:)` built a dictionary keyed only by `instance.serverURL`. If two accounts used the same Forgejo instance, `Dictionary(uniqueKeysWithValues:)` could trap on duplicate keys, and fallback lookup could also reuse the wrong account for a failed restore.

## Changes

- Add an account key based on normalized server URL + username.
- Use that key for bootstrapped connection fallback in `MultiInstanceManager`.
- Add a regression test covering two token-auth accounts on the same server URL.

## Verification

- `git diff --check` passes.
- I could not run `xcodebuild` locally because the available Xcode install has not accepted the license on this machine (`xcodebuild` exits before build/test execution).

Co-authored-by: Piotr Durlej <pdurlej@users.noreply.github.com>
Reviewed-on: https://codeberg.org/secana/Forji/pulls/29
Reviewed-by: secana <secana@noreply.codeberg.org>
2026-05-10 10:39:02 +02:00
Stefan Hausotte
f7eba701e2 chore: update Forji version 2026-05-07 19:28:42 +02:00
Stefan Hausotte
57bde0934f refactor: small name changes 2026-05-07 19:17:49 +02:00
Stefan Hausotte
2e07f6da4d test: make tests more stable 2026-05-07 19:07:40 +02:00
Stefan Hausotte
ea9c9825e0 test: fix race condition in tests 2026-05-07 18:21:09 +02:00
Voislav Vasiljevski
3d3de81f2b [Draft] feat: Actions tab — depends on ForgejoKit#1 (#28)
Closes #3.

Adds an "Actions" tab to the repository view, surfacing Forgejo Actions runs and (experimentally) their jobs, steps, and logs.

## What's in this branch

1. ~~**`chore: point ForgejoKit at local checkout for Actions PR`** — temporary `XCLocalSwiftPackageReference` so the feature commit compiles against the unreleased ForgejoKit changes. Drop this commit before merge and replace with `chore: bump ForgejoKit to <new version>` once the ForgejoKit PR ships in a release.~~

   **`chore: update ForgejoKit to released version 0.4.0`** + **`chore: add ForgejoKit 0.4.0 to Package.resolved`** — switches from local path override to a remote pin at `secana/ForgejoKit` `0.4.0`.

2. **`feat: add Actions tab to repository view`** — the actual feature.

## UI scope

- New `Actions` tab, shown only when `repository.hasActions == true`.
- Runs list with All / Running / Success / Failed filter, pagination, pull-to-refresh, empty state.
- Run detail with header, metadata (workflow file, event, trigger user, started/duration — zero-Date suppressed), and an experimental Jobs section.
- Job view with collapsible step rows; logs lazy-load on expand via `logCursors`.
- "Open in browser" toolbar item using each run's `html_url`.

## Experimental jobs/steps view

Forgejo's `/api/v1` doesn't expose jobs, steps, or step logs for a run. This PR opts into ForgejoKit's experimental `fetchRunView` (backed by Forgejo's web-UI route) so we can render a GitHub-Actions-style view today. The Jobs section and Steps screen are explicitly labelled "Experimental" in the UI, and a footer notes the output may change between Forgejo versions. Happy to drop this section if you'd rather ship public-API only first.

## Defensive handling

- Forgejo serialises an unset `time.Time` as `0001-01-01T00:00:00Z` (showed up as "Started 56 yrs, 4 mths" / "Duration 493 906h" on a cancelled run). Sanitised display helpers suppress any pre-2000 date.
- The experimental `fetchRunView` resolves Forgejo's `RedirectToLatestAttempt` server-side before POSTing, so attempts with non-zero attempt numbers work. (Without this, Forgejo returns 500 "task with job_id N and attempt 0: resource does not exist".)

## Tests

- Unit: `WorkflowRunFilterTests`, `WorkflowStatusIconTests` (filter mapping, status enum compatibility against Forgejo's documented values, status icon mapping, run helpers, zero-date guard).
- UI: `ActionsUITests` (read-only smoke test).

Co-authored-by: Voislav Vasiljevski <voislav@voioo.cz>
Reviewed-on: https://codeberg.org/secana/Forji/pulls/28
2026-05-07 18:06:17 +02:00
Stefan Hausotte
d7a883efda test: update forgejo to version 15 2026-04-20 16:52:59 +02:00
Stefan Hausotte
d879440577 fix: "dismiss" not available on notifactions in "All Instance" view 2026-04-14 18:46:34 +02:00
Stefan Hausotte
666b8e57c3 chore: update version 2026-04-01 13:49:54 +02:00
Stefan Hausotte
bf65e6d13d fix: multi-instance persistent cache 2026-03-31 23:09:18 +02:00
Stefan Hausotte
2c2b61e249 feat: filter for repos #16 2026-03-31 20:11:22 +02:00
secana
095adfb22a Merge pull request 'feat/persistent-cache' (#25) from feat/persistent-cache into main
Reviewed-on: https://codeberg.org/secana/Forji/pulls/25
2026-03-28 19:32:18 +01:00
Stefan Hausotte
25de1be2d9 feat: disable PR view for mirrored repos 2026-03-28 19:31:07 +01:00
Stefan Hausotte
5aa65525cf feat: persistent cache #17 2026-03-28 19:31:07 +01:00
Stefan Hausotte
c42ed9552e test: improve test runtime
Reviewed-on: https://codeberg.org/secana/Forji/pulls/24
Co-authored-by: Stefan Hausotte <stefan.hausotte@gmx.de>
Co-committed-by: Stefan Hausotte <stefan.hausotte@gmx.de>
2026-03-23 19:07:55 +01:00
Stefan Hausotte
253f3e88d1 feat: implement iOS notifications #6
Naive implementation for iOS notifications.

Problem: Forgejo does not support push notifications. We need to pull every  X minutes for new notifications. The even bigger problem: iOS does not support background polling. So this is more a "as good as possible" but not good approach.

Reviewed-on: https://codeberg.org/secana/Forji/pulls/23
Co-authored-by: Stefan Hausotte <stefan.hausotte@gmx.de>
Co-committed-by: Stefan Hausotte <stefan.hausotte@gmx.de>
2026-03-22 17:43:21 +01:00
Stefan Hausotte
92a928b2f4 fix: missing merge button
Co-authored-by: Stefan Hausotte <stefan.hausotte@gmx.de>
Co-committed-by: Stefan Hausotte <stefan.hausotte@gmx.de>
2026-03-22 11:54:24 +01:00
secana
07727b6f90 Merge pull request 'refactor: reduce code complexity' (#21) from refactor/reduce-complexity into main 2026-03-22 11:42:40 +01:00
Stefan Hausotte
556c614011 refactor: reduce code complexity (#19)
Co-authored-by: Stefan Hausotte <stefan.hausotte@gmx.de>
Co-committed-by: Stefan Hausotte <stefan.hausotte@gmx.de>
2026-03-21 15:57:34 +01:00
Stefan Hausotte
d61851b9e9 refactor: reduce code complexity 2026-03-21 15:52:53 +01:00
Stefan Hausotte
0bcca64d3a refactor: small changes 2026-03-21 15:23:44 +01:00
Stefan Hausotte
10bbed5596 feat: combined instance view (#18)
Co-authored-by: Stefan Hausotte <stefan.hausotte@gmx.de>
Co-committed-by: Stefan Hausotte <stefan.hausotte@gmx.de>
2026-03-21 15:03:28 +01:00