Files
nucleic-remote-ios/NucleicRemote/NucleicRemote/Views/ProjectsView.swift
T
abkslmandNucleic 9ac71bc302 Bottom Tab Bar Visibility Adjustment
Nucleic-Session: 747527BF-AEE9-4939-BE70-A14F1F3BBBA8
Co-authored-by: Nucleic <[email protected]>
2026-07-05 17:44:39 -07:00

78 lines
3.3 KiB
Swift

import SwiftUI
import NucleicProtocol
/// Projects overview (mirrors the Mac's per-project pages): each project with its session
/// counts; tap through to that project's sessions + a scoped composer.
struct ProjectsView: View {
@EnvironmentObject var store: RemoteStore
var body: some View {
NavigationStack {
Group {
if store.dashboard.projects.isEmpty {
ContentUnavailableView("No projects", systemImage: "folder",
description: Text("Add a project on the Mac to see it here."))
} else {
List(store.dashboard.projects) { project in
NavigationLink {
ProjectDetailView(project: project)
} label: {
HStack(spacing: 12) {
Image(systemName: "folder.fill").foregroundStyle(Palette.accent)
VStack(alignment: .leading, spacing: 2) {
Text(project.name).font(.body.weight(.medium))
Text(project.defaultBranch).font(.caption).foregroundStyle(.secondary)
}
Spacer()
if project.activeCount > 0 {
Text("\(project.activeCount) active")
.font(.caption2.bold())
.padding(.horizontal, 7).padding(.vertical, 3)
.background(Palette.active.opacity(0.18), in: Capsule())
.foregroundStyle(Palette.active)
}
Text("\(project.sessionCount)").foregroundStyle(.secondary)
}
}
}
}
}
.navigationTitle("Projects")
.refreshable { store.refreshSessions() }
}
}
}
struct ProjectDetailView: View {
@EnvironmentObject var store: RemoteStore
let project: WireProject
@State private var showNewChat = false
private var sessions: [WireSessionSummary] {
store.liveSessions
.filter { $0.projectID == project.id.rawValue }
.sorted(by: StatusStyle.attentionThenRecency)
}
var body: some View {
List {
Section("Sessions") {
if sessions.isEmpty {
Text("No sessions yet.").foregroundStyle(.secondary)
}
ForEach(sessions, id: \.sessionID) { summary in
NavigationLink {
SessionDetailView(sessionID: summary.sessionID)
} label: { SessionRow(summary: summary, showProjectName: false) }
}
}
}
.navigationTitle(project.name)
.navigationBarTitleDisplayMode(.inline)
.scrollDismissesKeyboard(.interactively)
// A floating "+" scoped to this project — the glass composer sheet opens pre-locked to it,
// replacing the old inline start-chat card at the top of the page.
.newChatAffordance(project: project, isPresented: $showNewChat)
}
}