New builds for iOS 1.0.3 and Android 1.5.1
This commit is contained in:
@@ -394,7 +394,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = OpenClimb/OpenClimb.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 8;
|
||||
CURRENT_PROJECT_VERSION = 9;
|
||||
DEVELOPMENT_TEAM = 4BC9Y2LL4B;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -437,7 +437,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = OpenClimb/OpenClimb.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 8;
|
||||
CURRENT_PROJECT_VERSION = 9;
|
||||
DEVELOPMENT_TEAM = 4BC9Y2LL4B;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -479,7 +479,7 @@
|
||||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
||||
CODE_SIGN_ENTITLEMENTS = SessionStatusLiveExtension.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 8;
|
||||
CURRENT_PROJECT_VERSION = 9;
|
||||
DEVELOPMENT_TEAM = 4BC9Y2LL4B;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = SessionStatusLive/Info.plist;
|
||||
@@ -509,7 +509,7 @@
|
||||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
||||
CODE_SIGN_ENTITLEMENTS = SessionStatusLiveExtension.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 8;
|
||||
CURRENT_PROJECT_VERSION = 9;
|
||||
DEVELOPMENT_TEAM = 4BC9Y2LL4B;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = SessionStatusLive/Info.plist;
|
||||
|
||||
Binary file not shown.
@@ -7,7 +7,7 @@
|
||||
<key>OpenClimb.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>0</integer>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
<key>SessionStatusLiveExtension.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
@@ -15,5 +15,18 @@
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>SuppressBuildableAutocreation</key>
|
||||
<dict>
|
||||
<key>D24C19672E75002A0045894C</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>D2FE948A2E78FEE0008CDB25</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -260,7 +260,7 @@ struct Problem: Identifiable, Codable, Hashable {
|
||||
let description: String?
|
||||
let climbType: ClimbType
|
||||
let difficulty: DifficultyGrade
|
||||
let setter: String?
|
||||
|
||||
let tags: [String]
|
||||
let location: String?
|
||||
let imagePaths: [String]
|
||||
@@ -272,7 +272,7 @@ struct Problem: Identifiable, Codable, Hashable {
|
||||
|
||||
init(
|
||||
gymId: UUID, name: String? = nil, description: String? = nil, climbType: ClimbType,
|
||||
difficulty: DifficultyGrade, setter: String? = nil, tags: [String] = [],
|
||||
difficulty: DifficultyGrade, tags: [String] = [],
|
||||
location: String? = nil, imagePaths: [String] = [], dateSet: Date? = nil,
|
||||
notes: String? = nil
|
||||
) {
|
||||
@@ -282,7 +282,7 @@ struct Problem: Identifiable, Codable, Hashable {
|
||||
self.description = description
|
||||
self.climbType = climbType
|
||||
self.difficulty = difficulty
|
||||
self.setter = setter
|
||||
|
||||
self.tags = tags
|
||||
self.location = location
|
||||
self.imagePaths = imagePaths
|
||||
@@ -296,7 +296,7 @@ struct Problem: Identifiable, Codable, Hashable {
|
||||
|
||||
func updated(
|
||||
name: String? = nil, description: String? = nil, climbType: ClimbType? = nil,
|
||||
difficulty: DifficultyGrade? = nil, setter: String? = nil, tags: [String]? = nil,
|
||||
difficulty: DifficultyGrade? = nil, tags: [String]? = nil,
|
||||
location: String? = nil, imagePaths: [String]? = nil, isActive: Bool? = nil,
|
||||
dateSet: Date? = nil, notes: String? = nil
|
||||
) -> Problem {
|
||||
@@ -307,7 +307,7 @@ struct Problem: Identifiable, Codable, Hashable {
|
||||
description: description ?? self.description,
|
||||
climbType: climbType ?? self.climbType,
|
||||
difficulty: difficulty ?? self.difficulty,
|
||||
setter: setter ?? self.setter,
|
||||
|
||||
tags: tags ?? self.tags,
|
||||
location: location ?? self.location,
|
||||
imagePaths: imagePaths ?? self.imagePaths,
|
||||
@@ -321,7 +321,7 @@ struct Problem: Identifiable, Codable, Hashable {
|
||||
|
||||
private init(
|
||||
id: UUID, gymId: UUID, name: String?, description: String?, climbType: ClimbType,
|
||||
difficulty: DifficultyGrade, setter: String?, tags: [String], location: String?,
|
||||
difficulty: DifficultyGrade, tags: [String], location: String?,
|
||||
imagePaths: [String], isActive: Bool, dateSet: Date?, notes: String?, createdAt: Date,
|
||||
updatedAt: Date
|
||||
) {
|
||||
@@ -331,7 +331,7 @@ struct Problem: Identifiable, Codable, Hashable {
|
||||
self.description = description
|
||||
self.climbType = climbType
|
||||
self.difficulty = difficulty
|
||||
self.setter = setter
|
||||
|
||||
self.tags = tags
|
||||
self.location = location
|
||||
self.imagePaths = imagePaths
|
||||
@@ -344,7 +344,7 @@ struct Problem: Identifiable, Codable, Hashable {
|
||||
|
||||
static func fromImport(
|
||||
id: UUID, gymId: UUID, name: String?, description: String?, climbType: ClimbType,
|
||||
difficulty: DifficultyGrade, setter: String?, tags: [String], location: String?,
|
||||
difficulty: DifficultyGrade, tags: [String], location: String?,
|
||||
imagePaths: [String], isActive: Bool, dateSet: Date?, notes: String?, createdAt: Date,
|
||||
updatedAt: Date
|
||||
) -> Problem {
|
||||
@@ -355,7 +355,7 @@ struct Problem: Identifiable, Codable, Hashable {
|
||||
description: description,
|
||||
climbType: climbType,
|
||||
difficulty: difficulty,
|
||||
setter: setter,
|
||||
|
||||
tags: tags,
|
||||
location: location,
|
||||
imagePaths: imagePaths,
|
||||
|
||||
@@ -475,7 +475,7 @@ class ClimbingDataManager: ObservableObject {
|
||||
|
||||
let exportData = ClimbDataExport(
|
||||
exportedAt: dateFormatter.string(from: Date()),
|
||||
version: "1.0",
|
||||
version: "2.0",
|
||||
gyms: gyms.map { AndroidGym(from: $0) },
|
||||
problems: problems.map { AndroidProblem(from: $0) },
|
||||
sessions: sessions.map { AndroidClimbSession(from: $0) },
|
||||
@@ -593,7 +593,7 @@ struct ClimbDataExport: Codable {
|
||||
let attempts: [AndroidAttempt]
|
||||
|
||||
init(
|
||||
exportedAt: String, version: String = "1.0", gyms: [AndroidGym], problems: [AndroidProblem],
|
||||
exportedAt: String, version: String = "2.0", gyms: [AndroidGym], problems: [AndroidProblem],
|
||||
sessions: [AndroidClimbSession], attempts: [AndroidAttempt]
|
||||
) {
|
||||
self.exportedAt = exportedAt
|
||||
@@ -675,7 +675,6 @@ struct AndroidProblem: Codable {
|
||||
let description: String?
|
||||
let climbType: ClimbType
|
||||
let difficulty: DifficultyGrade
|
||||
let setter: String?
|
||||
let tags: [String]
|
||||
let location: String?
|
||||
let imagePaths: [String]?
|
||||
@@ -692,7 +691,6 @@ struct AndroidProblem: Codable {
|
||||
self.description = problem.description
|
||||
self.climbType = problem.climbType
|
||||
self.difficulty = problem.difficulty
|
||||
self.setter = problem.setter
|
||||
self.tags = problem.tags
|
||||
self.location = problem.location
|
||||
self.imagePaths = problem.imagePaths.isEmpty ? nil : problem.imagePaths
|
||||
@@ -707,7 +705,7 @@ struct AndroidProblem: Codable {
|
||||
|
||||
init(
|
||||
id: String, gymId: String, name: String?, description: String?, climbType: ClimbType,
|
||||
difficulty: DifficultyGrade, setter: String? = nil, tags: [String] = [],
|
||||
difficulty: DifficultyGrade, tags: [String] = [],
|
||||
location: String? = nil,
|
||||
imagePaths: [String]? = nil, isActive: Bool = true, dateSet: String? = nil,
|
||||
notes: String? = nil,
|
||||
@@ -719,7 +717,6 @@ struct AndroidProblem: Codable {
|
||||
self.description = description
|
||||
self.climbType = climbType
|
||||
self.difficulty = difficulty
|
||||
self.setter = setter
|
||||
self.tags = tags
|
||||
self.location = location
|
||||
self.imagePaths = imagePaths
|
||||
@@ -746,7 +743,6 @@ struct AndroidProblem: Codable {
|
||||
description: description,
|
||||
climbType: climbType,
|
||||
difficulty: difficulty,
|
||||
setter: setter,
|
||||
tags: tags,
|
||||
location: location,
|
||||
imagePaths: imagePaths ?? [],
|
||||
@@ -766,7 +762,6 @@ struct AndroidProblem: Codable {
|
||||
description: self.description,
|
||||
climbType: self.climbType,
|
||||
difficulty: self.difficulty,
|
||||
setter: self.setter,
|
||||
tags: self.tags,
|
||||
location: self.location,
|
||||
imagePaths: newImagePaths.isEmpty ? nil : newImagePaths,
|
||||
@@ -1331,7 +1326,6 @@ extension ClimbingDataManager {
|
||||
description: "Technical overhang with small holds",
|
||||
climbType: .boulder,
|
||||
difficulty: DifficultyGrade(system: .vScale, grade: "V4"),
|
||||
setter: "John Doe",
|
||||
tags: ["technical", "overhang"],
|
||||
location: "Cave area"
|
||||
)
|
||||
|
||||
@@ -635,12 +635,6 @@ struct ProblemExpandedView: View {
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
|
||||
if let setter = problem.setter, !setter.isEmpty {
|
||||
Label(setter, systemImage: "person")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
|
||||
if let description = problem.description, !description.isEmpty {
|
||||
Text(description)
|
||||
.font(.body)
|
||||
|
||||
@@ -13,7 +13,6 @@ struct AddEditProblemView: View {
|
||||
@State private var selectedClimbType: ClimbType = .boulder
|
||||
@State private var selectedDifficultySystem: DifficultySystem = .vScale
|
||||
@State private var difficultyGrade = ""
|
||||
@State private var setter = ""
|
||||
@State private var location = ""
|
||||
@State private var tags = ""
|
||||
@State private var notes = ""
|
||||
@@ -63,7 +62,7 @@ struct AddEditProblemView: View {
|
||||
PhotosSection()
|
||||
ClimbTypeSection()
|
||||
DifficultySection()
|
||||
LocationAndSetterSection()
|
||||
LocationSection()
|
||||
TagsSection()
|
||||
AdditionalInfoSection()
|
||||
}
|
||||
@@ -158,7 +157,6 @@ struct AddEditProblemView: View {
|
||||
)
|
||||
}
|
||||
|
||||
TextField("Route Setter (Optional)", text: $setter)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,7 +279,7 @@ struct AddEditProblemView: View {
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func LocationAndSetterSection() -> some View {
|
||||
private func LocationSection() -> some View {
|
||||
Section("Location & Details") {
|
||||
TextField(
|
||||
"Location (Optional)", text: $location, prompt: Text("e.g., 'Cave area', 'Wall 3'"))
|
||||
@@ -334,25 +332,28 @@ struct AddEditProblemView: View {
|
||||
HStack(spacing: 12) {
|
||||
ForEach(imageData.indices, id: \.self) { index in
|
||||
if let uiImage = UIImage(data: imageData[index]) {
|
||||
Image(uiImage: uiImage)
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fill)
|
||||
.frame(width: 80, height: 80)
|
||||
.clipped()
|
||||
.cornerRadius(8)
|
||||
.overlay(alignment: .topTrailing) {
|
||||
Button(action: {
|
||||
imageData.remove(at: index)
|
||||
if index < imagePaths.count {
|
||||
imagePaths.remove(at: index)
|
||||
}
|
||||
}) {
|
||||
Image(systemName: "xmark.circle.fill")
|
||||
.foregroundColor(.red)
|
||||
.background(Circle().fill(.white))
|
||||
ZStack(alignment: .topTrailing) {
|
||||
Image(uiImage: uiImage)
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fill)
|
||||
.frame(width: 80, height: 80)
|
||||
.clipped()
|
||||
.cornerRadius(8)
|
||||
|
||||
Button(action: {
|
||||
imageData.remove(at: index)
|
||||
if index < imagePaths.count {
|
||||
imagePaths.remove(at: index)
|
||||
}
|
||||
.offset(x: 8, y: -8)
|
||||
}) {
|
||||
Image(systemName: "xmark.circle.fill")
|
||||
.foregroundColor(.red)
|
||||
.background(Circle().fill(.white))
|
||||
.font(.system(size: 18))
|
||||
}
|
||||
.offset(x: 4, y: -4)
|
||||
}
|
||||
.frame(width: 88, height: 88) // Extra space for button
|
||||
} else {
|
||||
RoundedRectangle(cornerRadius: 8)
|
||||
.fill(.gray.opacity(0.3))
|
||||
@@ -365,6 +366,7 @@ struct AddEditProblemView: View {
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 1)
|
||||
.padding(.vertical, 8)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -410,7 +412,7 @@ struct AddEditProblemView: View {
|
||||
selectedClimbType = problem.climbType
|
||||
selectedDifficultySystem = problem.difficulty.system
|
||||
difficultyGrade = problem.difficulty.grade
|
||||
setter = problem.setter ?? ""
|
||||
|
||||
location = problem.location ?? ""
|
||||
tags = problem.tags.joined(separator: ", ")
|
||||
notes = problem.notes ?? ""
|
||||
@@ -420,7 +422,7 @@ struct AddEditProblemView: View {
|
||||
// Load image data for preview
|
||||
imageData = []
|
||||
for imagePath in problem.imagePaths {
|
||||
if let data = try? Data(contentsOf: URL(fileURLWithPath: imagePath)) {
|
||||
if let data = ImageManager.shared.loadImageData(fromPath: imagePath) {
|
||||
imageData.append(data)
|
||||
}
|
||||
}
|
||||
@@ -479,7 +481,7 @@ struct AddEditProblemView: View {
|
||||
|
||||
let trimmedName = name.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
let trimmedDescription = description.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
let trimmedSetter = setter.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
|
||||
let trimmedLocation = location.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
let trimmedNotes = notes.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
let trimmedTags = tags.split(separator: ",").map {
|
||||
@@ -494,7 +496,7 @@ struct AddEditProblemView: View {
|
||||
description: trimmedDescription.isEmpty ? nil : trimmedDescription,
|
||||
climbType: selectedClimbType,
|
||||
difficulty: difficulty,
|
||||
setter: trimmedSetter.isEmpty ? nil : trimmedSetter,
|
||||
|
||||
tags: trimmedTags,
|
||||
location: trimmedLocation.isEmpty ? nil : trimmedLocation,
|
||||
imagePaths: imagePaths,
|
||||
@@ -510,7 +512,7 @@ struct AddEditProblemView: View {
|
||||
description: trimmedDescription.isEmpty ? nil : trimmedDescription,
|
||||
climbType: selectedClimbType,
|
||||
difficulty: difficulty,
|
||||
setter: trimmedSetter.isEmpty ? nil : trimmedSetter,
|
||||
|
||||
tags: trimmedTags,
|
||||
location: trimmedLocation.isEmpty ? nil : trimmedLocation,
|
||||
imagePaths: imagePaths,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct GymDetailView: View {
|
||||
@@ -60,8 +59,10 @@ struct GymDetailView: View {
|
||||
ToolbarItemGroup(placement: .navigationBarTrailing) {
|
||||
if gym != nil {
|
||||
Menu {
|
||||
Button("Edit Gym") {
|
||||
Button {
|
||||
// Navigate to edit view
|
||||
} label: {
|
||||
Label("Edit Gym", systemImage: "pencil")
|
||||
}
|
||||
|
||||
Button(role: .destructive) {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct ProblemDetailView: View {
|
||||
@@ -64,8 +63,10 @@ struct ProblemDetailView: View {
|
||||
ToolbarItemGroup(placement: .navigationBarTrailing) {
|
||||
if problem != nil {
|
||||
Menu {
|
||||
Button("Edit Problem") {
|
||||
Button {
|
||||
showingEditProblem = true
|
||||
} label: {
|
||||
Label("Edit Problem", systemImage: "pencil")
|
||||
}
|
||||
|
||||
Button(role: .destructive) {
|
||||
@@ -167,12 +168,6 @@ struct ProblemHeaderCard: View {
|
||||
.font(.body)
|
||||
}
|
||||
|
||||
if let setter = problem.setter, !setter.isEmpty {
|
||||
Text("Set by: \(setter)")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
|
||||
if !problem.tags.isEmpty {
|
||||
ScrollView(.horizontal, showsIndicators: false) {
|
||||
HStack(spacing: 8) {
|
||||
|
||||
@@ -280,7 +280,6 @@ struct SessionStatsCard: View {
|
||||
LazyVGrid(columns: Array(repeating: GridItem(.flexible()), count: 2), spacing: 16) {
|
||||
StatItem(label: "Total Attempts", value: "\(stats.totalAttempts)")
|
||||
StatItem(label: "Problems", value: "\(stats.uniqueProblemsAttempted)")
|
||||
StatItem(label: "Successful", value: "\(stats.successfulAttempts)")
|
||||
StatItem(label: "Completed", value: "\(stats.uniqueProblemsCompleted)")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct GymsView: View {
|
||||
@@ -49,7 +48,10 @@ struct GymsList: View {
|
||||
Button {
|
||||
gymToEdit = gym
|
||||
} label: {
|
||||
Label("Edit", systemImage: "pencil")
|
||||
HStack {
|
||||
Image(systemName: "pencil")
|
||||
Text("Edit")
|
||||
}
|
||||
}
|
||||
.tint(.blue)
|
||||
}
|
||||
|
||||
@@ -13,10 +13,9 @@ struct ProblemsView: View {
|
||||
// Apply search filter
|
||||
if !searchText.isEmpty {
|
||||
filtered = filtered.filter { problem in
|
||||
(problem.name?.localizedCaseInsensitiveContains(searchText) ?? false)
|
||||
return problem.name?.localizedCaseInsensitiveContains(searchText) ?? false
|
||||
|| (problem.description?.localizedCaseInsensitiveContains(searchText) ?? false)
|
||||
|| (problem.location?.localizedCaseInsensitiveContains(searchText) ?? false)
|
||||
|| (problem.setter?.localizedCaseInsensitiveContains(searchText) ?? false)
|
||||
|| problem.tags.contains { $0.localizedCaseInsensitiveContains(searchText) }
|
||||
}
|
||||
}
|
||||
@@ -31,7 +30,11 @@ struct ProblemsView: View {
|
||||
filtered = filtered.filter { $0.gymId == gym.id }
|
||||
}
|
||||
|
||||
return filtered.sorted { $0.updatedAt > $1.updatedAt }
|
||||
// Separate active and inactive problems
|
||||
let active = filtered.filter { $0.isActive }.sorted { $0.updatedAt > $1.updatedAt }
|
||||
let inactive = filtered.filter { !$0.isActive }.sorted { $0.updatedAt > $1.updatedAt }
|
||||
|
||||
return active + inactive
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
@@ -195,10 +198,23 @@ struct ProblemsList: View {
|
||||
Label("Delete", systemImage: "trash")
|
||||
}
|
||||
|
||||
Button {
|
||||
let updatedProblem = problem.updated(isActive: !problem.isActive)
|
||||
dataManager.updateProblem(updatedProblem)
|
||||
} label: {
|
||||
Label(
|
||||
problem.isActive ? "Mark as Reset" : "Mark as Active",
|
||||
systemImage: problem.isActive ? "xmark.circle" : "checkmark.circle")
|
||||
}
|
||||
.tint(.orange)
|
||||
|
||||
Button {
|
||||
problemToEdit = problem
|
||||
} label: {
|
||||
Label("Edit", systemImage: "pencil")
|
||||
HStack {
|
||||
Image(systemName: "pencil")
|
||||
Text("Edit")
|
||||
}
|
||||
}
|
||||
.tint(.blue)
|
||||
}
|
||||
@@ -239,6 +255,7 @@ struct ProblemRow: View {
|
||||
Text(problem.name ?? "Unnamed Problem")
|
||||
.font(.headline)
|
||||
.fontWeight(.semibold)
|
||||
.foregroundColor(problem.isActive ? .primary : .secondary)
|
||||
|
||||
Text(gym?.name ?? "Unknown Gym")
|
||||
.font(.subheadline)
|
||||
@@ -295,9 +312,9 @@ struct ProblemRow: View {
|
||||
}
|
||||
|
||||
if !problem.isActive {
|
||||
Text("Inactive")
|
||||
Text("Reset / No Longer Set")
|
||||
.font(.caption)
|
||||
.foregroundColor(.red)
|
||||
.foregroundColor(.orange)
|
||||
.fontWeight(.medium)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user