Skip to content

Commit

Permalink
Integrate header and footer views into the list view
Browse files Browse the repository at this point in the history
  • Loading branch information
ns-vasilev committed Aug 26, 2024
1 parent acbffab commit 680de16
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ struct LoadingViewModifier: ViewModifier {
// MARK: ViewModifier

func body(content: Content) -> some View {
VStack {
Group {
content

if isLoading {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,41 +7,88 @@ import BladeTCA
import ComposableArchitecture
import SwiftUI

// MARK: - PaginatorListView

public struct PaginatorListView<
State: Equatable & Identifiable,
Action: Equatable,
Header: View,
Body: View,
Footer: View,
PositionType: Equatable,
Request: Equatable
>: View {
// MARK: Types

private typealias StoreType = ViewStoreOf<PaginatorReducer<State, Action, PositionType, Request>>

@SwiftUI.State private var isLoading = false

// MARK: Properties

public let store: Store<PaginatorState<State, PositionType>, PaginatorAction<State, Action, Request>>
public let header: () -> Header
public let content: (State) -> Body
public let footer: () -> Footer

public init(
store: Store<PaginatorState<State, PositionType>, PaginatorAction<State, Action, Request>>,
content: @escaping (State) -> Body
@ViewBuilder header: @escaping () -> Header,
@ViewBuilder content: @escaping (State) -> Body,
@ViewBuilder footer: @escaping () -> Footer
) {
self.store = store
self.header = header
self.content = content
self.footer = footer
}

// MARK: View

public var body: some View {
WithViewStore(store, observe: { $0 }) { (viewStore: StoreType) in
List(viewStore.items) { item in
content(item)
.onAppear {
viewStore.send(.itemAppeared(item.id))
List {
Section {
header()
}

WithViewStore(store, observe: { $0 }) { (viewStore: StoreType) in
Section(content: {
ForEach(viewStore.items) { item in
content(item)
.onAppear {
viewStore.send(.itemAppeared(item.id))
}
}
})
}

Section {
footer()
}

Section {
EmptyView()
.modifier(LoadingViewModifier(isLoading: isLoading))
}
.modifier(LoadingViewModifier(isLoading: viewStore.isLoading && !viewStore.items.isEmpty))
}
}
}

public extension PaginatorListView where Header == EmptyView, Footer == EmptyView {
init(
store: Store<PaginatorState<State, PositionType>, PaginatorAction<State, Action, Request>>,
@ViewBuilder content: @escaping (State) -> Body
) {
self.init(store: store, header: { EmptyView() }, content: content, footer: { EmptyView() })
}
}

public extension PaginatorListView where Header: View, Footer == EmptyView {
init(
store: Store<PaginatorState<State, PositionType>, PaginatorAction<State, Action, Request>>,
header: @escaping () -> Header,
@ViewBuilder content: @escaping (State) -> Body
) {
self.init(store: store, header: header, content: content, footer: { EmptyView() })
}
}
33 changes: 33 additions & 0 deletions Sources/BladeTCA/Classes/Presentation/Views/PaginatorView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ public struct PaginatorView<
Action: Equatable,
PositionType: Equatable,
Request: Equatable,
Header: View,
Body: View,
Footer: View,
RowContent: View
>: View {
// MARK: Types
Expand All @@ -24,35 +26,66 @@ public struct PaginatorView<
// MARK: Properties

public let store: Store<PaginatorState<State, PositionType>, PaginatorAction<State, Action, Request>>
public let header: () -> Header
public let content: ([State], @escaping (State) -> AnyView) -> Body
public let footer: () -> Footer
public let rowContent: (State) -> RowContent

// MARK: Initialization

public init(
store: Store<PaginatorState<State, PositionType>, PaginatorAction<State, Action, Request>>,
@ViewBuilder header: @escaping () -> Header,
@ViewBuilder content: @escaping ([State], @escaping (State) -> AnyView) -> Body,
@ViewBuilder footer: @escaping () -> Footer,
@ViewBuilder rowContent: @escaping (State) -> RowContent
) {
self.store = store
self.header = header
self.content = content
self.footer = footer
self.rowContent = rowContent
}

// MARK: View

public var body: some View {
WithViewStore(store, observe: { $0 }) { (viewStore: StoreType) in
header()

content(viewStore.items.elements) { item in
rowContent(item)
.onAppear { viewStore.send(.itemAppeared(item.id)) }
.any
}
.modifier(LoadingViewModifier(isLoading: viewStore.isLoading && !viewStore.items.isEmpty))

footer()
}
}
}

public extension PaginatorView where Header == EmptyView, Footer == EmptyView {
init(
store: Store<PaginatorState<State, PositionType>, PaginatorAction<State, Action, Request>>,
@ViewBuilder content: @escaping ([State], @escaping (State) -> AnyView) -> Body,
@ViewBuilder rowContent: @escaping (State) -> RowContent
) {
self.init(store: store, header: { EmptyView() }, content: content, footer: { EmptyView() }, rowContent: rowContent)
}
}

public extension PaginatorView where Header: View, Footer == EmptyView {
init(
store: Store<PaginatorState<State, PositionType>, PaginatorAction<State, Action, Request>>,
header: @escaping () -> Header,
@ViewBuilder content: @escaping ([State], @escaping (State) -> AnyView) -> Body,
@ViewBuilder rowContent: @escaping (State) -> RowContent
) {
self.init(store: store, header: header, content: content, footer: { EmptyView() }, rowContent: rowContent)
}
}

// MARK: Extension

private extension View {
Expand Down

0 comments on commit 680de16

Please sign in to comment.