Show the phone's task list in the Mac's right pane from a list button
This commit is contained in:
@@ -53,6 +53,7 @@ final class Overlays {
|
||||
var leadFocus = 0
|
||||
var tabFocus = 0
|
||||
var projectFocus = false
|
||||
var taskList = false
|
||||
@ObservationIgnored var pickerFrame: CGRect = .zero
|
||||
@ObservationIgnored var menuHover = false
|
||||
var paneFrame: CGRect = .zero
|
||||
|
||||
@@ -192,7 +192,9 @@ private struct TaskPane: View {
|
||||
VStack(spacing: 0) {
|
||||
TaskBar()
|
||||
VStack(spacing: 0) {
|
||||
if store.selectedTaskId == nil {
|
||||
if overlays.taskList {
|
||||
TaskList()
|
||||
} else if store.selectedTaskId == nil {
|
||||
ProjectView()
|
||||
} else {
|
||||
TabStrip()
|
||||
@@ -208,6 +210,12 @@ private struct TaskPane: View {
|
||||
}
|
||||
.environment(\.paneAccent, accent)
|
||||
.environment(\.inTask, true)
|
||||
.onChange(of: store.reveals) {
|
||||
overlays.taskList = false
|
||||
}
|
||||
.onChange(of: store.focus) {
|
||||
overlays.taskList = false
|
||||
}
|
||||
.onGeometryChange(for: CGRect.self) { g in
|
||||
g.frame(in: .global)
|
||||
} action: { frame in
|
||||
@@ -260,7 +268,7 @@ struct TaskBar: View {
|
||||
var body: some View {
|
||||
let task = store.selectedTaskId.flatMap { store.findTask($0) }
|
||||
let project = store.focusedProject()
|
||||
let label = task.map { "\(project?.name ?? "") ▸ \($0.name)" } ?? project?.name ?? "No task"
|
||||
let label = overlays.taskList ? "All tasks" : task.map { "\(project?.name ?? "") ▸ \($0.name)" } ?? project?.name ?? "No task"
|
||||
HStack(spacing: 0) {
|
||||
Color.clear.frame(maxWidth: .infinity, maxHeight: 1)
|
||||
Button {
|
||||
@@ -290,6 +298,20 @@ struct TaskBar: View {
|
||||
PreviewLink(preview: p)
|
||||
}
|
||||
}
|
||||
#if os(macOS)
|
||||
Button {
|
||||
overlays.taskList.toggle()
|
||||
} label: {
|
||||
Image(systemName: "list.bullet")
|
||||
.font(Fonts.font(13, weight: 600))
|
||||
.foregroundStyle(accent)
|
||||
.padding(.vertical, 6)
|
||||
.contentShape(Rectangle())
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.help(overlays.taskList ? "Back to the task" : "Task list")
|
||||
.accessibilityLabel("Task list")
|
||||
#endif
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
|
||||
@@ -279,69 +279,9 @@ private struct ConnectionMark: View {
|
||||
}
|
||||
|
||||
private struct TaskListScreen: View {
|
||||
@Environment(Store.self) private var store
|
||||
@Environment(Overlays.self) private var overlays
|
||||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
ForEach(store.projects, id: \.id) { project in
|
||||
let active = (store.tasks[project.id] ?? []).filter { $0.status == .active }
|
||||
Button {
|
||||
store.sheet = nil
|
||||
store.focusProject(project.id)
|
||||
} label: {
|
||||
HStack(alignment: .firstTextBaseline, spacing: 6) {
|
||||
Text(project.name)
|
||||
.font(Fonts.font(13, weight: 700))
|
||||
Text(project.defaultBranch)
|
||||
.font(Fonts.font(11))
|
||||
.foregroundStyle(Palette.muted)
|
||||
Spacer(minLength: 0)
|
||||
Image(systemName: "chevron.right")
|
||||
.font(Fonts.font(11))
|
||||
.foregroundStyle(Palette.muted)
|
||||
}
|
||||
.padding(.horizontal, 10)
|
||||
.padding(.top, 16)
|
||||
.padding(.bottom, 6)
|
||||
.contentShape(Rectangle())
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
if active.isEmpty {
|
||||
Text("No active tasks.")
|
||||
.foregroundStyle(Palette.muted)
|
||||
.padding(.horizontal, 10)
|
||||
.padding(.vertical, 6)
|
||||
}
|
||||
TaskRows(active: active)
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 8)
|
||||
.padding(.bottom, 16)
|
||||
}
|
||||
.safeAreaInset(edge: .top, spacing: 0) {
|
||||
HStack {
|
||||
Spacer(minLength: 0)
|
||||
Button("New task") {
|
||||
overlays.openSwitcher(store)
|
||||
}
|
||||
.buttonStyle(WebButtonStyle())
|
||||
.onGeometryChange(for: CGRect.self) { g in
|
||||
g.frame(in: .named("window"))
|
||||
} action: { frame in
|
||||
overlays.pickerFrame = frame
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.vertical, 6)
|
||||
.background(Palette.bg)
|
||||
.overlay(alignment: .bottom) {
|
||||
Rectangle().fill(Palette.border).frame(height: 1)
|
||||
}
|
||||
}
|
||||
.background(Palette.bg)
|
||||
.toolbar(.hidden, for: .navigationBar)
|
||||
TaskList()
|
||||
.toolbar(.hidden, for: .navigationBar)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -215,6 +215,72 @@ private func rowIndent(_ task: ByreTask, _ depth: Int) -> CGFloat {
|
||||
task.parentTaskId != nil ? 20 * CGFloat(depth - 1) : 0
|
||||
}
|
||||
|
||||
struct TaskList: View {
|
||||
@Environment(Store.self) private var store
|
||||
@Environment(Overlays.self) private var overlays
|
||||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
ForEach(store.projects, id: \.id) { project in
|
||||
let active = (store.tasks[project.id] ?? []).filter { $0.status == .active }
|
||||
Button {
|
||||
store.sheet = nil
|
||||
store.focusProject(project.id)
|
||||
} label: {
|
||||
HStack(alignment: .firstTextBaseline, spacing: 6) {
|
||||
Text(project.name)
|
||||
.font(Fonts.font(13, weight: 700))
|
||||
Text(project.defaultBranch)
|
||||
.font(Fonts.font(11))
|
||||
.foregroundStyle(Palette.muted)
|
||||
Spacer(minLength: 0)
|
||||
Image(systemName: "chevron.right")
|
||||
.font(Fonts.font(11))
|
||||
.foregroundStyle(Palette.muted)
|
||||
}
|
||||
.padding(.horizontal, 10)
|
||||
.padding(.top, 16)
|
||||
.padding(.bottom, 6)
|
||||
.contentShape(Rectangle())
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
if active.isEmpty {
|
||||
Text("No active tasks.")
|
||||
.foregroundStyle(Palette.muted)
|
||||
.padding(.horizontal, 10)
|
||||
.padding(.vertical, 6)
|
||||
}
|
||||
TaskRows(active: active)
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 8)
|
||||
.padding(.bottom, 16)
|
||||
}
|
||||
.safeAreaInset(edge: .top, spacing: 0) {
|
||||
HStack {
|
||||
Spacer(minLength: 0)
|
||||
Button("New task") {
|
||||
overlays.openSwitcher(store)
|
||||
}
|
||||
.buttonStyle(WebButtonStyle())
|
||||
.onGeometryChange(for: CGRect.self) { g in
|
||||
g.frame(in: .named("window"))
|
||||
} action: { frame in
|
||||
overlays.pickerFrame = frame
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.vertical, 6)
|
||||
.background(Palette.bg)
|
||||
.overlay(alignment: .bottom) {
|
||||
Rectangle().fill(Palette.border).frame(height: 1)
|
||||
}
|
||||
}
|
||||
.background(Palette.bg)
|
||||
}
|
||||
}
|
||||
|
||||
struct TaskRows: View {
|
||||
let active: [ByreTask]
|
||||
@Environment(Store.self) private var store
|
||||
|
||||
Reference in New Issue
Block a user