feat: hero card layout in all pills, cache-while-loading, logo splash screen
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

- Every pill (F1, Tech, AI, Cloud, HomeLab…) now shows the same hero card
  layout as "All" — first story is the big featured card, rest follow as rows
- "All" retains its full multi-section front page (US / Tech / AI / Sport…)
- Topic pill headers use the pill name instead of "TOP STORIES"
- filteredStories now falls back to cached SwiftData rows while loading, so
  switching to F1 or Tech shows cached cards instantly instead of a blank list
- Logo splash screen (3 pill bars + jarvis wordmark) shows on launch and fades
  out after setup completes; minimum 1.3 s so it's always readable
- JarvisWordmark moved to Theme.swift so SplashView and feed can share it

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Robin Kutesa
2026-06-21 02:15:23 +03:00
parent 760e6a121c
commit 207b1827c7
3 changed files with 81 additions and 52 deletions

View File

@@ -5,6 +5,38 @@ import SwiftData
import UIKit
import BackgroundTasks
// MARK: - Logo + splash
/// The three-bar logo that mirrors the app icon.
struct JarvisLogoMark: View {
var width: CGFloat = 180
var body: some View {
VStack(alignment: .center, spacing: 8) {
// Orange (short) · White (longest) · Grey (medium) same ratios as icon
Capsule().fill(Palette.orange)
.frame(width: width * 0.48, height: width * 0.075)
.shadow(color: Palette.orange.opacity(0.55), radius: 10, y: 2)
Capsule().fill(Color.white)
.frame(width: width, height: width * 0.075)
Capsule().fill(Color(hex: "888888"))
.frame(width: width * 0.74, height: width * 0.075)
}
}
}
struct SplashView: View {
var body: some View {
ZStack {
Color(hex: "0A0A0A").ignoresSafeArea()
VStack(spacing: 28) {
JarvisLogoMark(width: 180)
JarvisWordmark(size: 34)
}
}
}
}
final class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
@@ -37,6 +69,7 @@ struct JarvisApp: App {
@StateObject private var connectivity = ConnectivitySettings.shared
@StateObject private var connManager = ConnectivityManager.shared
@StateObject private var notifications = NotificationManager.shared
@State private var showSplash = true
// Single ModelContainer instance shared with BackgroundRefreshManager.
private let container: ModelContainer = {
@@ -47,13 +80,22 @@ struct JarvisApp: App {
var body: some Scene {
WindowGroup {
Group {
if settings.isConfigured {
RootTabView()
ZStack {
if showSplash {
SplashView()
.transition(.opacity)
} else {
OnboardingView()
Group {
if settings.isConfigured {
RootTabView()
} else {
OnboardingView()
}
}
.transition(.opacity)
}
}
.animation(.easeOut(duration: 0.5), value: showSplash)
.preferredColorScheme(appearanceMode.colorScheme)
.environmentObject(settings)
.environmentObject(store)
@@ -62,13 +104,14 @@ struct JarvisApp: App {
.environmentObject(connManager)
.environmentObject(notifications)
.task {
// Wire the shared container before scheduling BG work.
BackgroundRefreshManager.container = container
BackgroundRefreshManager.scheduleFeedRefresh()
connManager.start()
await connManager.resolveAndActivate()
await notifications.bootstrap()
// Hold splash until setup is done, minimum 1.3 s for legibility.
try? await Task.sleep(nanoseconds: 1_300_000_000)
showSplash = false
}
}
.modelContainer(container)