Robin Kutesa a86284cb8a
Some checks failed
CI / Build · macos-14 · Debug (push) Has been cancelled
CI / Build · macos-15 · Debug (push) Has been cancelled
CI / Build · macos-14 · Release (push) Has been cancelled
CI / Build · macos-15 · Release (push) Has been cancelled
feat: Mark All as Read — bulk action with optimistic UI and rollback
APIClient:
- markAllRead(storyIds💊) — POST /me/read/bulk with device ID, timestamp,
  source label, and pill name so backend can record context

Models:
- PaginatedStories gains unreadCount, readSuppressedCount, lastReadSyncAt,
  activePill — backend now returns these so the UI can report honest state

SignalFeedView:
- Header "…" menu replaces the standalone settings button; menu contains
  "Mark all as read" (disabled while pending or when nothing is unread) and
  a Settings item
- markAllRead() captures all visible unread IDs, optimistically inserts
  ReadStory for each, shows toast ("Marked N stories as read"), then fires
  the bulk API; on failure deletes the optimistic ReadStory records and
  shows an honest error toast ("Sync failed. Stories may reappear until retry.")
- Toast auto-dismisses after 3.5s, appears at the bottom of the ZStack
  with a slide-up + opacity animation

Cross-pill suppression was already in place — backend applies
unread_story_condition() to every /stories request regardless of tags,
so stories marked read in All disappear from Tech, Sport, etc. on the
next pill fetch.

Co-Authored-By: Kutesir <tqwyy79vzn@privaterelay.appleid.com>
2026-06-23 12:47:28 +03:00
2026-06-18 18:04:59 +03:00
2026-06-18 18:04:59 +03:00
2026-06-18 18:04:59 +03:00

Jarvis (iOS)

A SwiftUI iOS client for a self-hosted RSS news-correlation platform. It shows stories ranked by a server-computed signal score, caches full articles for offline reading, and receives live updates over WebSocket.

This repo is the iOS app only. The backend (RSS ingest, clustering, scoring, REST + WebSocket) is a separate service — see docs/backend/ for the build/handoff spec and API/contract.md for the wire contract.

Architecture

  • REST on launch → WebSocket for live updates. The server computes signal scores; the app only renders them.
  • Offline-first via SwiftData (CachedStory, CachedArticle).
  • No auth — local network / self-hosted (reach it over LAN or Tailscale).
  • Data flows through StoryStore / dedicated view-models; views never call the API directly.
Jarvis/
  Models/        Codable + SwiftData models
  Networking/    APIClient (REST), WebSocketManager
  Store/         StoryStore, ServerSettings
  Connectivity/  ConnectivitySettings, ConnectivityManager (LAN ⇄ Tailscale)
  Views/
    Home/    Story/    Reader/    Feeds/    Connectivity/    Shared/

Requirements

  • Xcode 16+ (developed on Xcode 26), iOS 17.0+ deployment target (SwiftData).
  • XcodeGen — the .xcodeproj is generated from project.yml and is not committed.

Build & run

brew install xcodegen          # once
xcodegen generate              # produces Jarvis.xcodeproj
open Jarvis.xcodeproj          # ⌘R in Xcode, or:

xcodebuild -project Jarvis.xcodeproj -scheme Jarvis \
  -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 17' build

Connecting to a backend

On first launch, enter the server as host:port (the app prepends http:// and ws://), e.g. 10.10.1.70:8098. Remote access via Tailscale is configured in the in-app Connectivity → Remote Access card; the app auto-selects the LAN address when it's reachable and the Tailscale address when away.

CI

GitHub Actions builds a matrix of macOS runners × Debug/Release on every push and PR — see .github/workflows/ci.yml.

Status

Frontend complete and verified against a live backend (signal feed, story detail, article reader, feed manager, connectivity). Open work is tracked in Issues / docs/BACKLOG.md.

Description
No description provided
Readme 1.5 MiB
Languages
Swift 98.1%
Shell 1.9%