Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SwiftUIContributorView #162

Merged
merged 2 commits into from
Jul 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions app-ios/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ let package = Package(
.kmpClient,
.kmpModule,
.eventKitClient,
.model,
]
),
.testTarget(
Expand Down Expand Up @@ -194,6 +195,8 @@ let package = Package(
dependencies: [
.tca,
.kmpClient,
.theme,
.model,
]
),
.testTarget(
Expand All @@ -204,6 +207,7 @@ let package = Package(
]
),
.target(name: "CommonComponents", dependencies: [.theme, .kmpModule]),
.target(name: "Model"),
// Please run ./gradlew app-ios-shared:assembleSharedXCFramework first
.binaryTarget(name: "KmpModule", path: "../app-ios-shared/build/XCFrameworks/debug/shared.xcframework"),
]
Expand Down Expand Up @@ -235,6 +239,7 @@ extension Target.Dependency {
static let eventKitClient: Target.Dependency = "EventKitClient"
static let theme: Target.Dependency = "Theme"
static let commonComponents: Target.Dependency = "CommonComponents"
static let model: Target.Dependency = "Model"

static let firebaseAuth: Target.Dependency = .product(name: "FirebaseAuth", package: "firebase-ios-sdk")
static let firebaseRemoteConfig: Target.Dependency = .product(name: "FirebaseRemoteConfig", package: "firebase-ios-sdk")
Expand Down
2 changes: 1 addition & 1 deletion app-ios/Sources/AboutFeature/AboutReducer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public struct AboutReducer {
@CasePathable
public enum View {
case staffsTapped
case contributersTapped
case contributorsTapped
case sponsorsTapped
case codeOfConductTapped
case acknowledgementsTapped
Expand Down
2 changes: 1 addition & 1 deletion app-ios/Sources/AboutFeature/AboutView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public struct AboutView: View {
.background(Color(.outlineOutlineVariant))

Button(action: {
send(.contributersTapped)
send(.contributorsTapped)
}, label: {
Label(
String(localized: "Contributers", bundle: .module),
Expand Down
4 changes: 2 additions & 2 deletions app-ios/Sources/App/RootReducer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ public struct RootReducer {
state.paths.about.append(.staff(.init()))
return .none

case .about(.view(.contributersTapped)):
state.paths.about.append(.contributor(.init(text: "")))
case .about(.view(.contributorsTapped)):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix typo

state.paths.about.append(.contributor(.init()))
return .none

case .about(.view(.sponsorsTapped)):
Expand Down
5 changes: 5 additions & 0 deletions app-ios/Sources/App/RootView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ public struct RootView: View {
}
}
.tint(AssetColors.Custom.flamingo.swiftUIColor)
.navigationBarTitleStyle(
color: AssetColors.Surface.onSurface.swiftUIColor,
titleTextStyle: .titleMedium,
largeTitleTextStyle: .headlineSmall
)
Comment on lines +82 to +86
Copy link
Contributor Author

@shin-usu shin-usu Jul 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is custom modifier.
I looked Figma design, navigation title of all screens are same style, so I changed it in RootView.

}

@MainActor
Expand Down
52 changes: 46 additions & 6 deletions app-ios/Sources/ContributorFeature/ContributorReducer.swift
Original file line number Diff line number Diff line change
@@ -1,27 +1,67 @@
import ComposableArchitecture
import KMPClient
import Model
import Foundation
@preconcurrency import class shared.Contributor

@Reducer
public struct ContributorReducer {
public struct ContributorReducer: Sendable {
@Dependency(\.contributorClient) var contributorClient
public init() { }

@ObservableState
public struct State: Equatable {
var text: String
var contributors: [Contributor]?
var url: IdentifiableURL?

public init(text: String) {
self.text = text
public init(contributors: [Contributor]? = nil) {
self.contributors = contributors
}
}

public enum Action {
public enum Action: Sendable, BindableAction {
case binding(BindingAction<State>)
case onAppear
case response(Result<[Contributor], any Error>)
case view(View)

public enum View: Sendable {
case contributorButtonTapped(URL)
}
}

private enum CancelID { case request }

public var body: some ReducerOf<Self> {
BindingReducer()
Reduce { state, action in
switch action {
case .onAppear:
state.text = "Contributor Feature"
return .run { send in
do {
try await contributorClient.refresh()
for try await contributors in try contributorClient.streamContributors() {
await send(.response(.success(contributors)))
}
} catch {
await send(.response(.failure(error)))
}
}
.cancellable(id: CancelID.request)

case let .response(.success(contributors)):
state.contributors = contributors
return .none

case let .response(.failure(error)):
print(error.localizedDescription)
return .none

case let .view(.contributorButtonTapped(url)):
state.url = IdentifiableURL(url)
return .none

case .binding:
return .none
}
}
Expand Down
41 changes: 23 additions & 18 deletions app-ios/Sources/ContributorFeature/ContributorView.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import ComposableArchitecture
import KMPClient
import SwiftUI
import Theme
import shared
import CommonComponents

public struct ContributorView: View {
private enum ViewType: String, CaseIterable {
Expand All @@ -11,7 +14,7 @@ public struct ContributorView: View {
var title: String {
switch self {
case .swift:
"Swift"
"SwiftUI"

case .kmpPresenter:
"KMP Presenter"
Expand All @@ -24,20 +27,25 @@ public struct ContributorView: View {

@State private var viewType: ViewType = .swift

private let store: StoreOf<ContributorReducer>
@Bindable var store: StoreOf<ContributorReducer>

public init(store: StoreOf<ContributorReducer>) {
self.store = store
}

public var body: some View {
Group {
VStack(spacing: 0) {
Picker("", selection: $viewType) {
ForEach(ViewType.allCases, id: \.self) { segment in
Text(segment.title)
}
}
.pickerStyle(.segmented)
.padding(16)

switch viewType {
case .swift:
Text(store.text)
.onAppear {
store.send(.onAppear)
}
SwiftUIContributorView(store: store)

case .kmpPresenter:
KmpContributorView()
Expand All @@ -46,19 +54,16 @@ public struct ContributorView: View {
KmpContributorComposeViewControllerWrapper()
}
}
.toolbar {
ToolbarItem(placement: .principal) {
Picker("", selection: $viewType) {
ForEach(ViewType.allCases, id: \.self) { segment in
Text(segment.rawValue)
}
}
.pickerStyle(.segmented)
}
}
.background(AssetColors.Surface.surface.swiftUIColor)
.navigationTitle(String(localized: "Contributor", bundle: .module))
.navigationBarTitleDisplayMode(.large)
.sheet(item: $store.url, content: { url in
SafariView(url: url.id)
.ignoresSafeArea()
})
}
}

#Preview {
ContributorView(store: .init(initialState: .init(text: "Hoge"), reducer: { ContributorReducer() }))
ContributorView(store: .init(initialState: .init(), reducer: { ContributorReducer() }))
}
25 changes: 25 additions & 0 deletions app-ios/Sources/ContributorFeature/Resources/Localizable.xcstrings
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to share these strings between iOS and Android? We should move to a shared location if that is possible.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it was decided whether to share Resource between iOS and Android.
In fact, all iOS projects use the string catalog.
Therefore, it would be more efficient to replace all of them after the policy is decided.

"sourceLanguage" : "en",
"strings" : {
"" : {

},
"Contributor" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Contributor"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "コントリビューター"
}
}
}
}
},
"version" : "1.0"
}
60 changes: 60 additions & 0 deletions app-ios/Sources/ContributorFeature/SwiftUIContributorView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import SwiftUI
import ComposableArchitecture
import Theme

struct SwiftUIContributorView: View {
private let store: StoreOf<ContributorReducer>

public init(store: StoreOf<ContributorReducer>) {
self.store = store
}

var body: some View {
Group {
if let contributors = store.contributors {
ScrollView {
LazyVStack(spacing: 0) {
ForEach(contributors, id: \.id) { contributor in
Button {
if let urlString = contributor.profileUrl,
let url = URL(string: urlString) {
store.send(.view(.contributorButtonTapped(url)))
}
} label: {
HStack(alignment: .center, spacing: 12) {
AsyncImage(url: URL(string: contributor.iconUrl)) {
$0.image?.resizable()
}
.frame(width: 52, height: 52)
.clipShape(Circle())

Text(contributor.username)
.textStyle(.bodyLarge)
.foregroundStyle(AssetColors.Surface.onSurface.swiftUIColor)
.multilineTextAlignment(.leading)
.lineLimit(2)

Spacer()

}
.frame(maxWidth: .infinity)
.padding(.init(top: 12, leading: 16, bottom: 12, trailing: 16))
}
}
}
}
} else {
ProgressView()
.task {
store.send(.onAppear)
}
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(AssetColors.Surface.surface.swiftUIColor)
}
}

#Preview {
SwiftUIContributorView(store: .init(initialState: .init(), reducer: { ContributorReducer() }))
}
11 changes: 11 additions & 0 deletions app-ios/Sources/Model/IdentifiableURL.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import Foundation

public struct IdentifiableURL: Identifiable {
public let id: URL

public init(_ id: URL) {
self.id = id
}
}

extension IdentifiableURL: Equatable {}
16 changes: 16 additions & 0 deletions app-ios/Sources/Theme/View+Extension.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import SwiftUI

extension View {
public func navigationBarTitleStyle(color: Color, titleTextStyle: TextStyle, largeTitleTextStyle: TextStyle) -> some View {
let uiColor = UIColor(color)
UINavigationBar.appearance().titleTextAttributes = [
.foregroundColor: uiColor,
.font: titleTextStyle.font
]
UINavigationBar.appearance().largeTitleTextAttributes = [
.foregroundColor: uiColor,
.font: largeTitleTextStyle.font
]
return self
}
}
10 changes: 0 additions & 10 deletions app-ios/Sources/TimetableDetailFeature/Model/IdentifiableURL.swift

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import KMPClient
import shared
import Foundation
import EventKitClient
import Model

@Reducer
public struct TimetableDetailReducer: Sendable {
Expand Down
Loading