Skip to content

Commit

Permalink
test: 내가 푼 뉴스 보기 버튼 누르면 뉴스 목록이 떠야 함
Browse files Browse the repository at this point in the history
  • Loading branch information
greetings1012 committed Nov 22, 2023
1 parent af4367c commit f774666
Show file tree
Hide file tree
Showing 8 changed files with 404 additions and 10 deletions.
2 changes: 2 additions & 0 deletions Targets/D3N/Sources/Feature/MainTab/MainTabStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ struct MainTabStore: Reducer {
state.today = nil
state.myPage = nil
return .send(.delegate(.appleUnlinked))
default:
return .none
}

default:
Expand Down
18 changes: 11 additions & 7 deletions Targets/D3N/Sources/Feature/MyPage/Main/MyPageMainStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,26 @@ import ComposableArchitecture
public struct MyPageMainStore: Reducer {
public struct State: Equatable {
@PresentationState var alert: AlertState<Action.Alert>?

}

public enum Action: BindableAction, Equatable {
case binding(BindingAction<State>)

case solvedNewsButtonTapped

case unlinkButtonTapped

case appleUnlinkRequest
case appleUnlinkResponse(Result<Bool, D3NAPIError>)

case solvedNewsButtonTapped

case alert(PresentationAction<Alert>)
case delegate(Delegate)

public enum Delegate {
public enum Delegate: Equatable {
case unlinked
case solvedNewsButtonTapped
case select(NewsEntity)
}

public enum Alert: Equatable {
Expand All @@ -45,10 +48,7 @@ public struct MyPageMainStore: Reducer {

Reduce { state, action in
switch action {

case .solvedNewsButtonTapped:
return .none


case .unlinkButtonTapped:
state.alert = AlertState {
TextState("회원탈퇴는 되돌릴 수 없습니다.")
Expand All @@ -66,6 +66,8 @@ public struct MyPageMainStore: Reducer {
return .run { send in
await send(.appleUnlinkResponse(await authClient.appleUnlink()))
}
case .solvedNewsButtonTapped:
return .send(.delegate(.solvedNewsButtonTapped))

case .appleUnlinkResponse(.success), .appleUnlinkResponse(.failure):
return .send(.delegate(.unlinked))
Expand All @@ -84,5 +86,7 @@ public struct MyPageMainStore: Reducer {
}
}
.ifLet(\.$alert, action: /Action.alert)

}
}

11 changes: 9 additions & 2 deletions Targets/D3N/Sources/Feature/MyPage/Main/MyPageMainView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,17 @@ public struct MyPageMainView: View {

private func personalSection(viewStore: ViewStoreOf<MyPageMainStore>) -> some View {
Section {
Button("내가 푼 뉴스 보기", action: {
Button( action: {
viewStore.send(.solvedNewsButtonTapped)
}, label:{
Label(
title:{
Text("내가 푼 뉴스 보기")
}, icon: {
Image(systemName: "checkmark.circle.fill")
.foregroundStyle(.blue)
})
})
.foregroundStyle(.blue)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,52 @@ import ComposableArchitecture
public struct MyPageNavigationStackStore: Reducer {
public struct State: Equatable {
var main: MyPageMainStore.State = .init()
var solvedNews: SolvedNewsNavigationStackStore.State? = .init()
var path: StackState<Path.State> = .init()
}

public enum Action: BindableAction, Equatable {
case binding(BindingAction<State>)
case popToRoot
case path(StackAction<Path.State, Path.Action>)

case solvedNews(SolvedNewsNavigationStackStore.Action)

case main(MyPageMainStore.Action)
case delegate(Delegate)

public enum Delegate {
public enum Delegate: Equatable {
case unlinked
case select(NewsEntity)
}
}

public struct Path: Reducer {
public enum State: Equatable {
case detail(TodayDetailStore.State)
case quizMain(QuizMainStore.State)
case quizResult(QuizResultStore.State)
case solvedNews(SolvedNewsStore.State)
}

public enum Action: Equatable {
case detail(TodayDetailStore.Action)
case quizMain(QuizMainStore.Action)
case quizResult(QuizResultStore.Action)
case solvedNews(SolvedNewsStore.Action)
case popToRoot
}

public var body: some Reducer<State, Action> {
Scope(state: /State.quizMain, action: /Action.quizMain) {
QuizMainStore()
}
Scope(state: /State.quizResult, action: /Action.quizResult) {
QuizResultStore()
}
Scope(state: /State.solvedNews, action: /Action.solvedNews) {
SolvedNewsStore()
}
}
}

Expand All @@ -33,14 +69,47 @@ public struct MyPageNavigationStackStore: Reducer {
switch action {
case let .main(.delegate(action)):
switch action {
case let .select(newsEntity):
state.path.append(.quizMain(.init(newsEntity: newsEntity)))
return .none
case .unlinked:
return .send(.delegate(.unlinked))
case .solvedNewsButtonTapped:
state.path.append(.solvedNews(.init()))
return .none
}

case let .path(.element(id: _, action: .solvedNews(.delegate(action)))):
switch action {
case let .select(newsEntity):
state.path.append(.quizMain(.init(newsEntity: newsEntity)))
}
return .none

case let .path(.element(id: _, action: .quizMain(.delegate(action)))):
switch action {
case let .solved(quizEntityList):
state.path.append(.quizResult(.init(quizEntityList: quizEntityList)))
return .none
}

case let .path(.element(id: _, action: .quizResult(.delegate(action)))):
switch action {
case .backToRoot:
return .send(.popToRoot)
}

case .popToRoot:
state.path.removeAll()
return .none

default:
return .none
}
}
.ifLet(\.solvedNews, action: /Action.solvedNews) {
SolvedNewsNavigationStackStore()
}
Scope(state: \.main, action: /Action.main) {
MyPageMainStore()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
//
// SolvedNewsStore.swift
// D3N
//
// Created by Younghoon Ahn on 11/23/23.
// Copyright © 2023 sju. All rights reserved.
//

import Foundation

import ComposableArchitecture

public struct SolvedNewsStore: Reducer {
let FIXED_PAGE_SIZE = 10

public struct State: Equatable, Identifiable {
public let id: UUID

var newsEntityList: [NewsEntity] = []
var pageIndex: Int = 0

var newsListItems: IdentifiedArrayOf<NewsListItemCellStore.State> = []

public init(
id: UUID = .init()
) {
self.id = id
}
}

@Dependency(\.newsClient) var newsClient

public enum Action: Equatable {
case onAppear

case fetchNewsListRequest
case fetchNewsListResponse(Result<[NewsEntity], D3NAPIError>)

case newsListItems(id: NewsListItemCellStore.State.ID, action: NewsListItemCellStore.Action)
case delegate(Delegate)

public enum Delegate: Equatable {
case select(NewsEntity)
}
}

public var body: some ReducerOf<Self> {
Reduce { state, action in
switch action {
case .onAppear:
return .concatenate([
.send(.fetchNewsListRequest)
])

case .fetchNewsListRequest:
return .run { [pageIndex = state.pageIndex] send in
let response = await newsClient.fetchNewsList(pageIndex, FIXED_PAGE_SIZE)
await send(.fetchNewsListResponse(response))
}

case let .fetchNewsListResponse(.success(newsEntityList)):
let newsListItems = state.makeSolvedNewsListItems(from: newsEntityList)
state.newsListItems.append(contentsOf: newsListItems)
state.pageIndex += 1
return .none

case let .newsListItems(id: id, action: .delegate(action)):
switch action {
case .onAppear:
if id == state.newsListItems.ids.last {
return .send(.fetchNewsListRequest)
}
case .tapped:
if let newsEntity = state.newsListItems[id: id]?.newsEntity {
return .send(.delegate(.select(newsEntity)))
}
}
return .none

default:
return .none
}
}
.forEach(\.newsListItems, action: /Action.newsListItems(id:action:)) {
NewsListItemCellStore()
}
}
}

public extension SolvedNewsStore.State {
func makeSolvedNewsListItems(from newsEntityList: [NewsEntity]) -> IdentifiedArrayOf<NewsListItemCellStore.State> {
return .init(
uniqueElements: newsEntityList.map { newsEntity in
return .init(newsEntity: newsEntity)
}
)
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//
// SolvedNewsView.swift
// D3N
//
// Created by Younghoon Ahn on 11/23/23.
// Copyright © 2023 sju. All rights reserved.
//

import Foundation
import SwiftUI

import ComposableArchitecture

public struct SolvedNewsView: View {
let store: StoreOf<SolvedNewsStore>

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

public var body: some View {
WithViewStore(self.store, observe: { $0 }) { viewStore in
ScrollView {
VStack {
newsListItemsView
.padding(.horizontal)

Spacer()
}
}
.navigationTitle("내가 푼 뉴스")
.onAppear {
viewStore.send(.onAppear)
}
}
}

private var newsListItemsView: some View {
LazyVStack {
ForEachStore(self.store.scope(state: \.newsListItems, action: SolvedNewsStore.Action.newsListItems(id:action:))) {
NewsListItemCellView(store: $0)
}
}
}
}

Loading

0 comments on commit f774666

Please sign in to comment.