diff --git a/app-ios/Package.swift b/app-ios/Package.swift index 79e27a49d..a5b03b265 100644 --- a/app-ios/Package.swift +++ b/app-ios/Package.swift @@ -209,7 +209,9 @@ let package = Package( dependencies: [ .tca, .kmpClient, - .theme + .theme, + .model, + .commonComponents, ] ), .testTarget( diff --git a/app-ios/Sources/SponsorFeature/SponsorReducer.swift b/app-ios/Sources/SponsorFeature/SponsorReducer.swift index e377073e7..42cfe359e 100644 --- a/app-ios/Sources/SponsorFeature/SponsorReducer.swift +++ b/app-ios/Sources/SponsorFeature/SponsorReducer.swift @@ -14,21 +14,29 @@ public struct SponsorReducer : Sendable { var platinums = [Sponsor]() var golds = [Sponsor]() var supporters = [Sponsor]() + var url: IdentifiableURL? public init() { } } - public enum Action : Sendable { - case onAppear + public enum Action : Sendable, BindableAction { + case binding(BindingAction) case response(Result<[Sponsor], any Error>) + case view(View) + + public enum View: Sendable { + case onAppear + case sponsorTapped(URL) + } } public var body: some ReducerOf { + BindingReducer() Reduce { state, action in enum CancelID { case connection } switch action { - case .onAppear: + case .view(.onAppear): return .run { send in do { for try await sponsors in try sponsorsData.streamSponsors() { @@ -39,6 +47,11 @@ public struct SponsorReducer : Sendable { } } .cancellable(id: CancelID.connection) + + case let .view(.sponsorTapped(url)): + state.url = IdentifiableURL(url) + return .none + case .response(.success(let sponsors)): var platinums = [Sponsor]() var golds = [Sponsor]() @@ -59,9 +72,13 @@ public struct SponsorReducer : Sendable { state.golds = golds state.supporters = supporters return .none + case .response(.failure(let error)): print(error) return .none + + case .binding: + return .none } } } diff --git a/app-ios/Sources/SponsorFeature/SponsorView.swift b/app-ios/Sources/SponsorFeature/SponsorView.swift index d0e4a7240..052482c79 100644 --- a/app-ios/Sources/SponsorFeature/SponsorView.swift +++ b/app-ios/Sources/SponsorFeature/SponsorView.swift @@ -2,10 +2,10 @@ import ComposableArchitecture import SwiftUI import Theme import Model +import CommonComponents public struct SponsorView: View { - private let store: StoreOf - @State private var selectedSponsorData: Sponsor? + @Bindable private var store: StoreOf public init(store: StoreOf) { self.store = store @@ -39,10 +39,14 @@ public struct SponsorView: View { } .background(AssetColors.Surface.surface.swiftUIColor) .onAppear { - store.send(.onAppear) + store.send(.view(.onAppear)) } .navigationBarTitleDisplayMode(.large) .navigationTitle(String(localized: "Sponsor", bundle: .module)) + .sheet(item: $store.url, content: { url in + SafariView(url: url.id) + .ignoresSafeArea() + }) } @ViewBuilder @@ -53,15 +57,16 @@ public struct SponsorView: View { } LazyVGrid(columns: gridItems, spacing: 12) { ForEach(items) { item in - Button { - selectedSponsorData = item - } label: { - ZStack { - Color.white.clipShape(RoundedRectangle(cornerRadius: 12)) + ZStack { + Color.white.clipShape(RoundedRectangle(cornerRadius: 12)) + Button { + store.send(.view(.sponsorTapped(item.link))) + } label: { AsyncImage(url: item.logo) { $0.image? .resizable() .scaledToFit() + .frame(maxWidth: .infinity, maxHeight: .infinity) } .frame(height: imageHeight) .padding(.vertical, 6) diff --git a/app-ios/Tests/SponsorFeatureTests/SponsorFeatureTests.swift b/app-ios/Tests/SponsorFeatureTests/SponsorFeatureTests.swift index f22b5344a..31e81a259 100644 --- a/app-ios/Tests/SponsorFeatureTests/SponsorFeatureTests.swift +++ b/app-ios/Tests/SponsorFeatureTests/SponsorFeatureTests.swift @@ -1,5 +1,6 @@ import XCTest import ComposableArchitecture +import Model @testable import SponsorFeature final class SponsorFeatureTests: XCTestCase { @@ -20,7 +21,7 @@ final class SponsorFeatureTests: XCTestCase { } } } - await store.send(.onAppear) + await store.send(.view(.onAppear)) await store.receive(\.response.success) { $0.platinums = [ .init(id: "Hoge", logo: .init(string: "https://avatars.githubusercontent.com/u/10727543?s=200&v=4")!, link: .init(string: "https://2024.droidkaigi.jp/")!, plan: .platinum) @@ -34,4 +35,14 @@ final class SponsorFeatureTests: XCTestCase { } } + @MainActor + func testSponsorTapped() async throws { + let url = URL(string: "https://github.com/DroidKaigi/conference-app-2024")! + let store = TestStore(initialState: SponsorReducer.State()) { + SponsorReducer() + } + await store.send(.view(.sponsorTapped(url))) { + $0.url = IdentifiableURL(url) + } + } }