[Android] 1.9.2
All checks were successful
OpenClimb Docker Deploy / build-and-push (push) Successful in 2m29s

This commit is contained in:
2025-10-12 20:41:39 -06:00
parent 405fb06d5d
commit 30d2b3938e
23 changed files with 620 additions and 1721 deletions

View File

@@ -9,8 +9,26 @@ struct ProblemsView: View {
@State private var showingSearch = false
@FocusState private var isSearchFocused: Bool
private var filteredProblems: [Problem] {
var filtered = dataManager.problems
@State private var cachedFilteredProblems: [Problem] = []
private func updateFilteredProblems() {
Task(priority: .userInitiated) {
let result = await computeFilteredProblems()
// Switch back to the main thread to update the UI
await MainActor.run {
cachedFilteredProblems = result
}
}
}
private func computeFilteredProblems() async -> [Problem] {
// Capture dependencies for safe background processing
let problems = dataManager.problems
let searchText = self.searchText
let selectedClimbType = self.selectedClimbType
let selectedGym = self.selectedGym
var filtered = problems
// Apply search filter
if !searchText.isEmpty {
@@ -93,19 +111,19 @@ struct ProblemsView: View {
FilterSection(
selectedClimbType: $selectedClimbType,
selectedGym: $selectedGym,
filteredProblems: filteredProblems
filteredProblems: cachedFilteredProblems
)
.padding()
.background(.regularMaterial)
}
if filteredProblems.isEmpty {
if cachedFilteredProblems.isEmpty {
EmptyProblemsView(
isEmpty: dataManager.problems.isEmpty,
isFiltered: !dataManager.problems.isEmpty
)
} else {
ProblemsList(problems: filteredProblems)
ProblemsList(problems: cachedFilteredProblems)
}
}
}
@@ -158,6 +176,21 @@ struct ProblemsView: View {
AddEditProblemView()
}
}
.onAppear {
updateFilteredProblems()
}
.onChange(of: dataManager.problems) {
updateFilteredProblems()
}
.onChange(of: searchText) {
updateFilteredProblems()
}
.onChange(of: selectedClimbType) {
updateFilteredProblems()
}
.onChange(of: selectedGym) {
updateFilteredProblems()
}
}
}
@@ -269,6 +302,7 @@ struct ProblemsList: View {
@EnvironmentObject var dataManager: ClimbingDataManager
@State private var problemToDelete: Problem?
@State private var problemToEdit: Problem?
@State private var animationKey = 0
var body: some View {
List(problems, id: \.id) { problem in
@@ -309,8 +343,11 @@ struct ProblemsList: View {
}
.animation(
.spring(response: 0.5, dampingFraction: 0.8, blendDuration: 0.1),
value: problems.map { "\($0.id):\($0.isActive)" }.joined()
value: animationKey
)
.onChange(of: problems) {
animationKey += 1
}
.listStyle(.plain)
.scrollContentBackground(.hidden)
.scrollIndicators(.hidden)
@@ -344,6 +381,12 @@ struct ProblemRow: View {
dataManager.gym(withId: problem.gymId)
}
private var isCompleted: Bool {
dataManager.attempts.contains { attempt in
attempt.problemId == problem.id && attempt.result.isSuccessful
}
}
var body: some View {
VStack(alignment: .leading, spacing: 8) {
HStack {
@@ -361,10 +404,24 @@ struct ProblemRow: View {
Spacer()
VStack(alignment: .trailing, spacing: 4) {
Text(problem.difficulty.grade)
.font(.title2)
.fontWeight(.bold)
.foregroundColor(.blue)
HStack(spacing: 8) {
if !problem.imagePaths.isEmpty {
Image(systemName: "photo")
.font(.system(size: 14, weight: .medium))
.foregroundColor(.blue)
}
if isCompleted {
Image(systemName: "checkmark.circle.fill")
.font(.system(size: 14, weight: .medium))
.foregroundColor(.green)
}
Text(problem.difficulty.grade)
.font(.title2)
.fontWeight(.bold)
.foregroundColor(.blue)
}
Text(problem.climbType.displayName)
.font(.caption)
@@ -396,17 +453,6 @@ struct ProblemRow: View {
}
}
if !problem.imagePaths.isEmpty {
ScrollView(.horizontal, showsIndicators: false) {
LazyHStack(spacing: 8) {
ForEach(problem.imagePaths.prefix(3), id: \.self) { imagePath in
ProblemImageView(imagePath: imagePath)
}
}
.padding(.horizontal, 4)
}
}
if !problem.isActive {
Text("Reset / No Longer Set")
.font(.caption)
@@ -478,17 +524,6 @@ struct EmptyProblemsView: View {
}
}
struct ProblemImageView: View {
let imagePath: String
var body: some View {
OrientationAwareImage.fill(imagePath: imagePath)
.frame(width: 60, height: 60)
.clipped()
.cornerRadius(8)
}
}
#Preview {
ProblemsView()
.environmentObject(ClimbingDataManager.preview)