diff --git a/Example/VoticeDemo/VoticeDemo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Example/VoticeDemo/VoticeDemo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 81af845..fd1dc09 100644 --- a/Example/VoticeDemo/VoticeDemo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Example/VoticeDemo/VoticeDemo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -6,8 +6,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/SimplyDanny/SwiftLintPlugins", "state" : { - "revision" : "ef264ee3ccea175f5c9efc8b814ccb48cd3fdaac", - "version" : "0.62.1" + "revision" : "ecafb5b056cb7995324f0d310a61f30661d00cad", + "version" : "0.62.2" } } ], diff --git a/README.md b/README.md index 22ed2fa..f4c5c87 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ The Votice management app for handling suggestions or issues and apps is availab Add this line to your `Package.swift` dependencies: ```swift -.package(url: "https://github.com/artcc/votice-sdk", from: "1.0.17"), +.package(url: "https://github.com/artcc/votice-sdk", from: "1.0.18"), ``` Or via Xcode: diff --git a/Sources/Votice/Core/Managers/ConfigurationManager.swift b/Sources/Votice/Core/Managers/ConfigurationManager.swift index a99185f..fd873fc 100644 --- a/Sources/Votice/Core/Managers/ConfigurationManager.swift +++ b/Sources/Votice/Core/Managers/ConfigurationManager.swift @@ -51,7 +51,7 @@ final class ConfigurationManager: ConfigurationManagerProtocol, @unchecked Senda private let lock = NSLock() private let _baseURL = "https://api.votice.app/api" private let _configurationId = UUID().uuidString - private let _version = "1.0.17" + private let _version = "1.0.18" private let _buildNumber = "1" // MARK: - Public properties diff --git a/Sources/Votice/UI/Components/StatusBadge.swift b/Sources/Votice/UI/Components/StatusBadge.swift index d2fa954..61135c2 100644 --- a/Sources/Votice/UI/Components/StatusBadge.swift +++ b/Sources/Votice/UI/Components/StatusBadge.swift @@ -13,7 +13,27 @@ struct StatusBadge: View { @Environment(\.voticeTheme) private var theme - private var statusColor: Color { + let status: SuggestionStatusEntity + let progress: Int? + let useLiquidGlass: Bool + + // MARK: - View + + var body: some View { + if status == .inProgress, let progress { + inProgressTagView(with: progress) + } else { + defaultTagView + } + } +} + +// MARK: - Private + +private extension StatusBadge { + // MARK: - Properties + + var statusColor: Color { switch status { case .accepted: return theme.colors.accepted @@ -29,7 +49,8 @@ struct StatusBadge: View { return theme.colors.rejected } } - private var statusText: String { + + var statusText: String { let texts = TextManager.shared.texts switch status { @@ -40,6 +61,10 @@ struct StatusBadge: View { case .completed: return texts.completed case .inProgress: + if let progress { + return "\(texts.inProgress) \(progress)%" + } + return texts.inProgress case .pending: return texts.pending @@ -48,21 +73,47 @@ struct StatusBadge: View { } } - let status: SuggestionStatusEntity - let useLiquidGlass: Bool - - // MARK: - View - - var body: some View { + var defaultTagView: some View { Text(statusText) .font(theme.typography.caption) .foregroundColor(.white) - .padding(.horizontal, theme.spacing.sm) .padding(.vertical, theme.spacing.xs) + .padding(.horizontal, theme.spacing.sm) .adaptiveGlassBackground( useLiquidGlass: useLiquidGlass, cornerRadius: theme.cornerRadius.sm, fillColor: statusColor ) } + + // MARK: - Functions + + func inProgressTagView(with progress: Int) -> some View { + let fullText = statusText + let splitIndex = Int(Double(fullText.count) * Double(progress) / 100.0) + 1 + let completedPart = String(fullText.prefix(splitIndex)) + let remainingPart = String(fullText.dropFirst(splitIndex)) + + return Text("\(Text(completedPart).foregroundStyle(.white))\(Text(remainingPart).foregroundStyle(statusColor))") + .font(theme.typography.caption) + .padding(.vertical, theme.spacing.xs) + .padding(.horizontal, theme.spacing.sm) + .background { + GeometryReader { geometry in + ZStack(alignment: .leading) { + RoundedRectangle(cornerRadius: theme.cornerRadius.sm) + .fill(statusColor.opacity(0.3)) + RoundedRectangle(cornerRadius: theme.cornerRadius.sm) + .fill( + LinearGradient( + gradient: Gradient(colors: [statusColor, statusColor.opacity(0.7)]), + startPoint: .leading, + endPoint: .trailing + ) + ) + .frame(width: geometry.size.width * CGFloat(progress) / 100.0) + } + } + } + } } diff --git a/Sources/Votice/UI/Components/SuggestionCard.swift b/Sources/Votice/UI/Components/SuggestionCard.swift index 85d0a5f..15ee9f5 100644 --- a/Sources/Votice/UI/Components/SuggestionCard.swift +++ b/Sources/Votice/UI/Components/SuggestionCard.swift @@ -81,8 +81,12 @@ struct SuggestionCard: View { ) ) .scaleEffect(isPressed ? 1.005 : 1.0) - StatusBadge(status: suggestion.status ?? .pending, useLiquidGlass: useLiquidGlass) - .padding(theme.spacing.sm) + StatusBadge( + status: suggestion.status ?? .pending, + progress: suggestion.progress, + useLiquidGlass: useLiquidGlass + ) + .padding(theme.spacing.sm) } .onTapGesture { isPressed = true diff --git a/Sources/Votice/UI/Components/TVOSSuggestionCard.swift b/Sources/Votice/UI/Components/TVOSSuggestionCard.swift index c7460ef..360a100 100644 --- a/Sources/Votice/UI/Components/TVOSSuggestionCard.swift +++ b/Sources/Votice/UI/Components/TVOSSuggestionCard.swift @@ -70,7 +70,11 @@ struct TVOSSuggestionCard: View { } .frame(maxWidth: .infinity, alignment: .leading) VStack { - StatusBadge(status: suggestion.status ?? .pending, useLiquidGlass: useLiquidGlass) + StatusBadge( + status: suggestion.status ?? .pending, + progress: suggestion.progress, + useLiquidGlass: useLiquidGlass + ) Spacer() } } diff --git a/Sources/Votice/UI/Views/SuggestionDetail/SuggestionDetailView+tvOS.swift b/Sources/Votice/UI/Views/SuggestionDetail/SuggestionDetailView+tvOS.swift index 374a179..b96bb72 100644 --- a/Sources/Votice/UI/Views/SuggestionDetail/SuggestionDetailView+tvOS.swift +++ b/Sources/Votice/UI/Views/SuggestionDetail/SuggestionDetailView+tvOS.swift @@ -66,7 +66,11 @@ extension SuggestionDetailView { .foregroundColor(theme.colors.onSurface) .multilineTextAlignment(.leading) Spacer() - StatusBadge(status: currentSuggestion.status ?? .pending, useLiquidGlass: false) + StatusBadge( + status: currentSuggestion.status ?? .pending, + progress: currentSuggestion.progress, + useLiquidGlass: false) + } if let description = currentSuggestion.description, description != currentSuggestion.title { Text(description) diff --git a/Sources/Votice/UI/Views/SuggestionDetail/SuggestionDetailView.swift b/Sources/Votice/UI/Views/SuggestionDetail/SuggestionDetailView.swift index 48c125e..123660c 100644 --- a/Sources/Votice/UI/Views/SuggestionDetail/SuggestionDetailView.swift +++ b/Sources/Votice/UI/Views/SuggestionDetail/SuggestionDetailView.swift @@ -248,8 +248,12 @@ private extension SuggestionDetailView { .fill(theme.colors.surface) .shadow(color: .black.opacity(0.1), radius: 2, x: 0, y: 1) ) - StatusBadge(status: currentSuggestion.status ?? .pending, useLiquidGlass: viewModel.liquidGlassEnabled) - .padding(theme.spacing.sm) + StatusBadge( + status: currentSuggestion.status ?? .pending, + progress: suggestion.progress, + useLiquidGlass: viewModel.liquidGlassEnabled + ) + .padding(theme.spacing.sm) } } diff --git a/Tests/VoticeTests/Managers/ConfigurationManagerTests.swift b/Tests/VoticeTests/Managers/ConfigurationManagerTests.swift index 5294625..ab9d93b 100644 --- a/Tests/VoticeTests/Managers/ConfigurationManagerTests.swift +++ b/Tests/VoticeTests/Managers/ConfigurationManagerTests.swift @@ -32,7 +32,7 @@ struct ConfigurationManagerTests { #expect(manager.showCompletedSeparately == false) #expect(manager.user.isPremium == false) #expect(manager.optionalVisibleStatuses == [.accepted, .blocked, .rejected]) - #expect(manager.version == "1.0.17") + #expect(manager.version == "1.0.18") #expect(manager.buildNumber == "1") } @@ -138,7 +138,7 @@ struct ConfigurationManagerTests { // ConfigurationId, version, and buildNumber should remain unchanged #expect(!manager.configurationId.isEmpty) - #expect(manager.version == "1.0.17") + #expect(manager.version == "1.0.18") #expect(manager.buildNumber == "1") } @@ -325,7 +325,7 @@ struct ConfigurationManagerTests { // Test immutable properties #expect(manager.baseURL == "https://api.votice.app/api") - #expect(manager.version == "1.0.17") + #expect(manager.version == "1.0.18") #expect(manager.buildNumber == "1") // Configuration ID should be consistent for same instance