Add swipe hint for empty first column in task kanban

Shows a subtle "Swipe to see your tasks" hint centered on the first
column (Overdue) when it's empty but other columns have tasks. The
hint uses the theme primary color and scrolls with the column.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Trey t
2025-12-15 22:04:31 -06:00
parent e7c09f687a
commit 2517435551
3 changed files with 103 additions and 57 deletions

View File

@@ -24926,6 +24926,10 @@
"comment" : "The title of an alert that appears when a user successfully upgrades to a premium subscription.",
"isCommentAutoGenerated" : true
},
"Swipe to see your tasks" : {
"comment" : "A hint displayed in the \"Tasks\" section of the app, encouraging users to swipe to view their tasks.",
"isCommentAutoGenerated" : true
},
"Take your home management\nto the next level" : {
},

View File

@@ -15,6 +15,12 @@ struct TasksSection: View {
tasksResponse.columns.allSatisfy { $0.tasks.isEmpty }
}
private var shouldShowSwipeHint: Bool {
guard let firstColumn = tasksResponse.columns.first else { return false }
let hasTasksInOtherColumns = tasksResponse.columns.dropFirst().contains { !$0.tasks.isEmpty }
return firstColumn.tasks.isEmpty && hasTasksInOtherColumns
}
var body: some View {
VStack(alignment: .leading, spacing: 12) {
Text("Tasks")
@@ -28,32 +34,38 @@ struct TasksSection: View {
GeometryReader { geometry in
ScrollView(.horizontal, showsIndicators: false) {
LazyHStack(spacing: 16) {
// Dynamically create columns from response
ForEach(Array(tasksResponse.columns.enumerated()), id: \.element.name) { index, column in
DynamicTaskColumnView(
column: column,
onEditTask: { task in
onEditTask(task)
},
onCancelTask: { task in
onCancelTask(task)
},
onUncancelTask: { taskId in
onUncancelTask(taskId)
},
onMarkInProgress: { taskId in
onMarkInProgress(taskId)
},
onCompleteTask: { task in
onCompleteTask(task)
},
onArchiveTask: { task in
onArchiveTask(task)
},
onUnarchiveTask: { taskId in
onUnarchiveTask(taskId)
ZStack {
DynamicTaskColumnView(
column: column,
onEditTask: { task in
onEditTask(task)
},
onCancelTask: { task in
onCancelTask(task)
},
onUncancelTask: { taskId in
onUncancelTask(taskId)
},
onMarkInProgress: { taskId in
onMarkInProgress(taskId)
},
onCompleteTask: { task in
onCompleteTask(task)
},
onArchiveTask: { task in
onArchiveTask(task)
},
onUnarchiveTask: { taskId in
onUnarchiveTask(taskId)
}
)
// Show swipe hint on first column when it's empty but others have tasks
if index == 0 && shouldShowSwipeHint {
SwipeHintView()
}
)
}
.frame(width: geometry.size.width - 48)
}
}
@@ -68,6 +80,23 @@ struct TasksSection: View {
}
}
struct SwipeHintView: View {
var body: some View {
HStack(spacing: 8) {
Text("Swipe to see your tasks")
.font(.subheadline)
.foregroundColor(Color.appPrimary)
Image(systemName: "arrow.right")
.font(.subheadline)
.foregroundColor(Color.appPrimary)
}
.padding(.horizontal, 16)
.padding(.vertical, 10)
.background(Color.appPrimary.opacity(0.1))
.cornerRadius(20)
}
}
#Preview {
TasksSection(
tasksResponse: TaskColumnsResponse(

View File

@@ -31,6 +31,13 @@ struct AllTasksView: View {
private var isLoadingTasks: Bool { taskViewModel.isLoadingTasks }
private var tasksError: String? { taskViewModel.tasksError }
private var shouldShowSwipeHint: Bool {
guard let response = tasksResponse,
let firstColumn = response.columns.first else { return false }
let hasTasksInOtherColumns = response.columns.dropFirst().contains { !$0.tasks.isEmpty }
return firstColumn.tasks.isEmpty && hasTasksInOtherColumns
}
var body: some View {
mainContent
.sheet(isPresented: $showAddTask) {
@@ -232,45 +239,51 @@ struct AllTasksView: View {
LazyHGrid(rows: [
GridItem(.flexible(), spacing: 16)
], spacing: 16) {
// Dynamically create columns from response
ForEach(Array(tasksResponse.columns.enumerated()), id: \.element.name) { index, column in
DynamicTaskColumnView(
column: column,
onEditTask: { task in
selectedTaskForEdit = task
showEditTask = true
},
onCancelTask: { task in
selectedTaskForCancel = task
showCancelConfirmation = true
},
onUncancelTask: { taskId in
taskViewModel.uncancelTask(id: taskId) { _ in
loadAllTasks()
}
},
onMarkInProgress: { taskId in
taskViewModel.markInProgress(id: taskId) { success in
if success {
ZStack {
DynamicTaskColumnView(
column: column,
onEditTask: { task in
selectedTaskForEdit = task
showEditTask = true
},
onCancelTask: { task in
selectedTaskForCancel = task
showCancelConfirmation = true
},
onUncancelTask: { taskId in
taskViewModel.uncancelTask(id: taskId) { _ in
loadAllTasks()
}
},
onMarkInProgress: { taskId in
taskViewModel.markInProgress(id: taskId) { success in
if success {
loadAllTasks()
}
}
},
onCompleteTask: { task in
selectedTaskForComplete = task
},
onArchiveTask: { task in
selectedTaskForArchive = task
showArchiveConfirmation = true
},
onUnarchiveTask: { taskId in
taskViewModel.unarchiveTask(id: taskId) { _ in
loadAllTasks()
}
}
},
onCompleteTask: { task in
selectedTaskForComplete = task
},
onArchiveTask: { task in
selectedTaskForArchive = task
showArchiveConfirmation = true
},
onUnarchiveTask: { taskId in
taskViewModel.unarchiveTask(id: taskId) { _ in
loadAllTasks()
}
)
// Show swipe hint on first column when it's empty but others have tasks
if index == 0 && shouldShowSwipeHint {
SwipeHintView()
}
)
}
.frame(width: 350)
.id(index) // Add ID for ScrollViewReader
.id(index)
.scrollTransition { content, phase in
content
.opacity(phase.isIdentity ? 1 : 0.8)